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

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 (1026) hide show
  1. package/CHANGELOG.md +2548 -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 +494 -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 +317 -0
  24. package/dist/commands/archive/plan/gate.js.map +1 -0
  25. package/dist/commands/archive/plan/landscape.js +161 -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 +251 -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 +194 -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 +236 -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 +250 -0
  246. package/dist/commands/validate/service/service.js.map +1 -0
  247. package/dist/commands/validate/service/specs.js +219 -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 +334 -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 +47 -0
  392. package/dist/core/brief/unchecked.js.map +1 -0
  393. package/dist/core/c4/arch.js +282 -0
  394. package/dist/core/c4/arch.js.map +1 -0
  395. package/dist/core/c4/likec4.js +88 -0
  396. package/dist/core/c4/likec4.js.map +1 -0
  397. package/dist/core/c4/parsed/dynamic-views.js +132 -0
  398. package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
  399. package/dist/core/c4/parsed/specification.js +78 -0
  400. package/dist/core/c4/parsed/specification.js.map +1 -0
  401. package/dist/core/c4/parsed/values.js +57 -0
  402. package/dist/core/c4/parsed/values.js.map +1 -0
  403. package/dist/core/c4/parsed/view-ids.js +75 -0
  404. package/dist/core/c4/parsed/view-ids.js.map +1 -0
  405. package/dist/core/c4/project/architecture.js +45 -0
  406. package/dist/core/c4/project/architecture.js.map +1 -0
  407. package/dist/core/c4/project/documents.js +58 -0
  408. package/dist/core/c4/project/documents.js.map +1 -0
  409. package/dist/core/c4/project/load.js +183 -0
  410. package/dist/core/c4/project/load.js.map +1 -0
  411. package/dist/core/c4/resolve/service.js +113 -0
  412. package/dist/core/c4/resolve/service.js.map +1 -0
  413. package/dist/core/c4/seed/fleet-file.js +201 -0
  414. package/dist/core/c4/seed/fleet-file.js.map +1 -0
  415. package/dist/core/c4/seed/items.js +91 -0
  416. package/dist/core/c4/seed/items.js.map +1 -0
  417. package/dist/core/c4/seed/stamp.js +48 -0
  418. package/dist/core/c4/seed/stamp.js.map +1 -0
  419. package/dist/core/c4/seed/template.js +109 -0
  420. package/dist/core/c4/seed/template.js.map +1 -0
  421. package/dist/core/c4/source-mask.js +82 -0
  422. package/dist/core/c4/source-mask.js.map +1 -0
  423. package/dist/core/c4/source-scan.js +227 -0
  424. package/dist/core/c4/source-scan.js.map +1 -0
  425. package/dist/core/c4/splice/authored-source.js +101 -0
  426. package/dist/core/c4/splice/authored-source.js.map +1 -0
  427. package/dist/core/c4/splice/contract.js +10 -0
  428. package/dist/core/c4/splice/contract.js.map +1 -0
  429. package/dist/core/c4/splice/delta-blocks.js +126 -0
  430. package/dist/core/c4/splice/delta-blocks.js.map +1 -0
  431. package/dist/core/c4/splice/identity/edges.js +36 -0
  432. package/dist/core/c4/splice/identity/edges.js.map +1 -0
  433. package/dist/core/c4/splice/landscape-merge.js +269 -0
  434. package/dist/core/c4/splice/landscape-merge.js.map +1 -0
  435. package/dist/core/c4/splice/placement.js +194 -0
  436. package/dist/core/c4/splice/placement.js.map +1 -0
  437. package/dist/core/c4/workspace.js +172 -0
  438. package/dist/core/c4/workspace.js.map +1 -0
  439. package/dist/core/capabilities/authoring/notes.js +141 -0
  440. package/dist/core/capabilities/authoring/notes.js.map +1 -0
  441. package/dist/core/capabilities/capabilities.js +124 -0
  442. package/dist/core/capabilities/capabilities.js.map +1 -0
  443. package/dist/core/capabilities/delta/doc.js +50 -0
  444. package/dist/core/capabilities/delta/doc.js.map +1 -0
  445. package/dist/core/capabilities/delta/overlay.js +129 -0
  446. package/dist/core/capabilities/delta/overlay.js.map +1 -0
  447. package/dist/core/capabilities/delta/summary.js +57 -0
  448. package/dist/core/capabilities/delta/summary.js.map +1 -0
  449. package/dist/core/capabilities/delta/tree.js +43 -0
  450. package/dist/core/capabilities/delta/tree.js.map +1 -0
  451. package/dist/core/capabilities/delta/uncovered.js +222 -0
  452. package/dist/core/capabilities/delta/uncovered.js.map +1 -0
  453. package/dist/core/capabilities/findings.js +342 -0
  454. package/dist/core/capabilities/findings.js.map +1 -0
  455. package/dist/core/capabilities/realizes/findings.js +181 -0
  456. package/dist/core/capabilities/realizes/findings.js.map +1 -0
  457. package/dist/core/capabilities/realizes/join.js +143 -0
  458. package/dist/core/capabilities/realizes/join.js.map +1 -0
  459. package/dist/core/capabilities/rollup.js +199 -0
  460. package/dist/core/capabilities/rollup.js.map +1 -0
  461. package/dist/core/capabilities/tree.js +126 -0
  462. package/dist/core/capabilities/tree.js.map +1 -0
  463. package/dist/core/capabilities/usecase-join.js +277 -0
  464. package/dist/core/capabilities/usecase-join.js.map +1 -0
  465. package/dist/core/coherence/authoring/scaffold.js +177 -0
  466. package/dist/core/coherence/authoring/scaffold.js.map +1 -0
  467. package/dist/core/coherence/authoring/sentinels.js +74 -0
  468. package/dist/core/coherence/authoring/sentinels.js.map +1 -0
  469. package/dist/core/coherence/coherence.js +251 -0
  470. package/dist/core/coherence/coherence.js.map +1 -0
  471. package/dist/core/coherence/declared.js +269 -0
  472. package/dist/core/coherence/declared.js.map +1 -0
  473. package/dist/core/coherence/events/declared.js +206 -0
  474. package/dist/core/coherence/events/declared.js.map +1 -0
  475. package/dist/core/coherence/events/events.js +204 -0
  476. package/dist/core/coherence/events/events.js.map +1 -0
  477. package/dist/core/coherence/events/grades.js +166 -0
  478. package/dist/core/coherence/events/grades.js.map +1 -0
  479. package/dist/core/coherence/events/lookups.js +122 -0
  480. package/dist/core/coherence/events/lookups.js.map +1 -0
  481. package/dist/core/coherence/events/merged.js +40 -0
  482. package/dist/core/coherence/events/merged.js.map +1 -0
  483. package/dist/core/coherence/living.js +198 -0
  484. package/dist/core/coherence/living.js.map +1 -0
  485. package/dist/core/coherence/lookups.js +155 -0
  486. package/dist/core/coherence/lookups.js.map +1 -0
  487. package/dist/core/coherence/pending.js +39 -0
  488. package/dist/core/coherence/pending.js.map +1 -0
  489. package/dist/core/conflict-markers.js +60 -0
  490. package/dist/core/conflict-markers.js.map +1 -0
  491. package/dist/core/delta/claims.js +73 -0
  492. package/dist/core/delta/claims.js.map +1 -0
  493. package/dist/core/delta/delta.js +296 -0
  494. package/dist/core/delta/delta.js.map +1 -0
  495. package/dist/core/delta/document.js +177 -0
  496. package/dist/core/delta/document.js.map +1 -0
  497. package/dist/core/delta/scope.js +48 -0
  498. package/dist/core/delta/scope.js.map +1 -0
  499. package/dist/core/delta/select.js +237 -0
  500. package/dist/core/delta/select.js.map +1 -0
  501. package/dist/core/dependencies/dependencies.js +141 -0
  502. package/dist/core/dependencies/dependencies.js.map +1 -0
  503. package/dist/core/dependencies/facts.js +165 -0
  504. package/dist/core/dependencies/facts.js.map +1 -0
  505. package/dist/core/dependencies/fanin.js +88 -0
  506. package/dist/core/dependencies/fanin.js.map +1 -0
  507. package/dist/core/dependencies/graph.js +106 -0
  508. package/dist/core/dependencies/graph.js.map +1 -0
  509. package/dist/core/diff/base-git.js +148 -0
  510. package/dist/core/diff/base-git.js.map +1 -0
  511. package/dist/core/diff/base-state.js +209 -0
  512. package/dist/core/diff/base-state.js.map +1 -0
  513. package/dist/core/diff/current-state.js +96 -0
  514. package/dist/core/diff/current-state.js.map +1 -0
  515. package/dist/core/diff/scope/changed-paths.js +188 -0
  516. package/dist/core/diff/scope/changed-paths.js.map +1 -0
  517. package/dist/core/diff/semantic.js +243 -0
  518. package/dist/core/diff/semantic.js.map +1 -0
  519. package/dist/core/diff/victims.js +247 -0
  520. package/dist/core/diff/victims.js.map +1 -0
  521. package/dist/core/docs.js +7 -64
  522. package/dist/core/docs.js.map +1 -1
  523. package/dist/core/doctor/agents.js +222 -0
  524. package/dist/core/doctor/agents.js.map +1 -0
  525. package/dist/core/doctor/config.js +135 -0
  526. package/dist/core/doctor/config.js.map +1 -0
  527. package/dist/core/doctor/doctor.js +275 -0
  528. package/dist/core/doctor/doctor.js.map +1 -0
  529. package/dist/core/doctor/report.js +13 -0
  530. package/dist/core/doctor/report.js.map +1 -0
  531. package/dist/core/doctor/residue.js +110 -0
  532. package/dist/core/doctor/residue.js.map +1 -0
  533. package/dist/core/document/apply.js +77 -0
  534. package/dist/core/document/apply.js.map +1 -0
  535. package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
  536. package/dist/core/document/frontmatter.js.map +1 -0
  537. package/dist/core/document/grammar/directives.js +143 -0
  538. package/dist/core/document/grammar/directives.js.map +1 -0
  539. package/dist/core/document/parse.js +263 -0
  540. package/dist/core/document/parse.js.map +1 -0
  541. package/dist/core/document/scenarios.js +221 -0
  542. package/dist/core/document/scenarios.js.map +1 -0
  543. package/dist/core/document/spec.js +241 -0
  544. package/dist/core/document/spec.js.map +1 -0
  545. package/dist/core/{config.js → envelope/config.js} +79 -18
  546. package/dist/core/envelope/config.js.map +1 -0
  547. package/dist/core/envelope/json.js +175 -0
  548. package/dist/core/envelope/json.js.map +1 -0
  549. package/dist/core/{version.js → envelope/version.js} +3 -3
  550. package/dist/core/envelope/version.js.map +1 -0
  551. package/dist/core/explain/families.js +175 -0
  552. package/dist/core/explain/families.js.map +1 -0
  553. package/dist/core/explain/fix-tables.js +178 -0
  554. package/dist/core/explain/fix-tables.js.map +1 -0
  555. package/dist/core/explain/lookup.js +181 -0
  556. package/dist/core/explain/lookup.js.map +1 -0
  557. package/dist/core/explain/refusals.js +49 -0
  558. package/dist/core/explain/refusals.js.map +1 -0
  559. package/dist/core/explain/terms.js +144 -0
  560. package/dist/core/explain/terms.js.map +1 -0
  561. package/dist/core/explore/describe.js +105 -0
  562. package/dist/core/explore/describe.js.map +1 -0
  563. package/dist/core/{explore.js → explore/explore.js} +50 -101
  564. package/dist/core/explore/explore.js.map +1 -0
  565. package/dist/core/fleet-context.js +120 -78
  566. package/dist/core/fleet-context.js.map +1 -1
  567. package/dist/core/gate/checks.js +201 -0
  568. package/dist/core/gate/checks.js.map +1 -0
  569. package/dist/core/gate/partners.js +205 -0
  570. package/dist/core/gate/partners.js.map +1 -0
  571. package/dist/core/gate/report.js +47 -0
  572. package/dist/core/gate/report.js.map +1 -0
  573. package/dist/core/gate/verification.js +113 -0
  574. package/dist/core/gate/verification.js.map +1 -0
  575. package/dist/core/gherkin/digest.js +60 -0
  576. package/dist/core/gherkin/digest.js.map +1 -0
  577. package/dist/core/gherkin/emit.js +177 -0
  578. package/dist/core/gherkin/emit.js.map +1 -0
  579. package/dist/core/gherkin/read.js +76 -0
  580. package/dist/core/gherkin/read.js.map +1 -0
  581. package/dist/core/gherkin/stale.js +234 -0
  582. package/dist/core/gherkin/stale.js.map +1 -0
  583. package/dist/core/gherkin/stamp.js +77 -0
  584. package/dist/core/gherkin/stamp.js.map +1 -0
  585. package/dist/core/gherkin/steps/catalogue.js +154 -0
  586. package/dist/core/gherkin/steps/catalogue.js.map +1 -0
  587. package/dist/core/gherkin/steps/inventory.js +98 -0
  588. package/dist/core/gherkin/steps/inventory.js.map +1 -0
  589. package/dist/core/gherkin/steps/phrase.js +72 -0
  590. package/dist/core/gherkin/steps/phrase.js.map +1 -0
  591. package/dist/core/glossary/delta.js +106 -0
  592. package/dist/core/glossary/delta.js.map +1 -0
  593. package/dist/core/glossary/tree.js +86 -0
  594. package/dist/core/glossary/tree.js.map +1 -0
  595. package/dist/core/kernel/concurrency.js.map +1 -0
  596. package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
  597. package/dist/core/kernel/document-bytes.js.map +1 -0
  598. package/dist/core/kernel/fences.js +42 -0
  599. package/dist/core/kernel/fences.js.map +1 -0
  600. package/dist/core/kernel/ids/capability.js +72 -0
  601. package/dist/core/kernel/ids/capability.js.map +1 -0
  602. package/dist/core/kernel/ids/dirs.js +33 -0
  603. package/dist/core/kernel/ids/dirs.js.map +1 -0
  604. package/dist/core/kernel/ids/feature.js +75 -0
  605. package/dist/core/kernel/ids/feature.js.map +1 -0
  606. package/dist/core/kernel/ids/service.js +156 -0
  607. package/dist/core/kernel/ids/service.js.map +1 -0
  608. package/dist/core/kernel/ids/subsystem.js +63 -0
  609. package/dist/core/kernel/ids/subsystem.js.map +1 -0
  610. package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
  611. package/dist/core/kernel/path-safety.js.map +1 -0
  612. package/dist/core/kernel/records.js.map +1 -0
  613. package/dist/core/links/backlinks.js +68 -0
  614. package/dist/core/links/backlinks.js.map +1 -0
  615. package/dist/core/links/case.js +74 -0
  616. package/dist/core/links/case.js.map +1 -0
  617. package/dist/core/links/corpus.js +104 -0
  618. package/dist/core/links/corpus.js.map +1 -0
  619. package/dist/core/links/findings.js +110 -0
  620. package/dist/core/links/findings.js.map +1 -0
  621. package/dist/core/links/parse.js +236 -0
  622. package/dist/core/links/parse.js.map +1 -0
  623. package/dist/core/mcp/argv.js +111 -0
  624. package/dist/core/mcp/argv.js.map +1 -0
  625. package/dist/core/mcp/author-tools.js +72 -0
  626. package/dist/core/mcp/author-tools.js.map +1 -0
  627. package/dist/core/mcp/framing.js +46 -0
  628. package/dist/core/mcp/framing.js.map +1 -0
  629. package/dist/core/mcp/protocol.js +280 -0
  630. package/dist/core/mcp/protocol.js.map +1 -0
  631. package/dist/core/mcp/tools.js +314 -0
  632. package/dist/core/mcp/tools.js.map +1 -0
  633. package/dist/core/obligations/obligations.js +106 -0
  634. package/dist/core/obligations/obligations.js.map +1 -0
  635. package/dist/core/openapi/baseline/gate.js +166 -0
  636. package/dist/core/openapi/baseline/gate.js.map +1 -0
  637. package/dist/core/openapi/baseline/plan.js +112 -0
  638. package/dist/core/openapi/baseline/plan.js.map +1 -0
  639. package/dist/core/openapi/baseline/record.js +175 -0
  640. package/dist/core/openapi/baseline/record.js.map +1 -0
  641. package/dist/core/openapi/depth.js +122 -0
  642. package/dist/core/openapi/depth.js.map +1 -0
  643. package/dist/core/openapi/digest.js +189 -0
  644. package/dist/core/openapi/digest.js.map +1 -0
  645. package/dist/core/{openapi.js → openapi/doc.js} +44 -106
  646. package/dist/core/openapi/doc.js.map +1 -0
  647. package/dist/core/openapi/merge/components.js +234 -0
  648. package/dist/core/openapi/merge/components.js.map +1 -0
  649. package/dist/core/openapi/merge/error.js +26 -0
  650. package/dist/core/openapi/merge/error.js.map +1 -0
  651. package/dist/core/openapi/merge/markers.js +208 -0
  652. package/dist/core/openapi/merge/markers.js.map +1 -0
  653. package/dist/core/openapi/merge/merge.js +256 -0
  654. package/dist/core/openapi/merge/merge.js.map +1 -0
  655. package/dist/core/openapi/merge/pin.js +128 -0
  656. package/dist/core/openapi/merge/pin.js.map +1 -0
  657. package/dist/core/openspec/decide/capabilities.js +244 -0
  658. package/dist/core/openspec/decide/capabilities.js.map +1 -0
  659. package/dist/core/openspec/decide/dispositions.js +63 -0
  660. package/dist/core/openspec/decide/dispositions.js.map +1 -0
  661. package/dist/core/openspec/decide/renames.js +179 -0
  662. package/dist/core/openspec/decide/renames.js.map +1 -0
  663. package/dist/core/openspec/decide/verdict.js +84 -0
  664. package/dist/core/openspec/decide/verdict.js.map +1 -0
  665. package/dist/core/openspec/inventory.js +221 -0
  666. package/dist/core/openspec/inventory.js.map +1 -0
  667. package/dist/core/openspec/model/mapping.js +76 -0
  668. package/dist/core/openspec/model/mapping.js.map +1 -0
  669. package/dist/core/openspec/model/model.js +65 -0
  670. package/dist/core/openspec/model/model.js.map +1 -0
  671. package/dist/core/openspec/scan/changes.js +235 -0
  672. package/dist/core/openspec/scan/changes.js.map +1 -0
  673. package/dist/core/openspec/scan/config.js +200 -0
  674. package/dist/core/openspec/scan/config.js.map +1 -0
  675. package/dist/core/openspec/scan/shape.js +260 -0
  676. package/dist/core/openspec/scan/shape.js.map +1 -0
  677. package/dist/core/openspec/scan/walk.js +133 -0
  678. package/dist/core/openspec/scan/walk.js.map +1 -0
  679. package/dist/core/openspec/scan/workspace.js +71 -0
  680. package/dist/core/openspec/scan/workspace.js.map +1 -0
  681. package/dist/core/owners/codeowners.js +141 -0
  682. package/dist/core/owners/codeowners.js.map +1 -0
  683. package/dist/core/pack/features.js +120 -0
  684. package/dist/core/pack/features.js.map +1 -0
  685. package/dist/core/pack/joins.js +112 -0
  686. package/dist/core/pack/joins.js.map +1 -0
  687. package/dist/core/pack/living.js +118 -0
  688. package/dist/core/pack/living.js.map +1 -0
  689. package/dist/core/pack/pack.js +76 -0
  690. package/dist/core/pack/pack.js.map +1 -0
  691. package/dist/core/permissions/permissions.js +87 -0
  692. package/dist/core/permissions/permissions.js.map +1 -0
  693. package/dist/core/projection/api.js +84 -0
  694. package/dist/core/projection/api.js.map +1 -0
  695. package/dist/core/projection/arch-slice.js +63 -0
  696. package/dist/core/projection/arch-slice.js.map +1 -0
  697. package/dist/core/projection/events.js +34 -0
  698. package/dist/core/projection/events.js.map +1 -0
  699. package/dist/core/provenance/findings.js +208 -0
  700. package/dist/core/provenance/findings.js.map +1 -0
  701. package/dist/core/provenance/git.js +225 -0
  702. package/dist/core/provenance/git.js.map +1 -0
  703. package/dist/core/provenance/gitq/moves.js +182 -0
  704. package/dist/core/provenance/gitq/moves.js.map +1 -0
  705. package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
  706. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
  707. package/dist/core/provenance/sample/scope.js +238 -0
  708. package/dist/core/provenance/sample/scope.js.map +1 -0
  709. package/dist/core/provenance/sample/sections.js +61 -0
  710. package/dist/core/provenance/sample/sections.js.map +1 -0
  711. package/dist/core/provenance/sources.js +263 -0
  712. package/dist/core/provenance/sources.js.map +1 -0
  713. package/dist/core/provenance/stamp.js +232 -0
  714. package/dist/core/provenance/stamp.js.map +1 -0
  715. package/dist/core/provenance/walk.js +157 -0
  716. package/dist/core/provenance/walk.js.map +1 -0
  717. package/dist/core/repo/authored/paths.js +192 -0
  718. package/dist/core/repo/authored/paths.js.map +1 -0
  719. package/dist/core/repo/entries.js +126 -0
  720. package/dist/core/repo/entries.js.map +1 -0
  721. package/dist/core/repo/paths.js +236 -0
  722. package/dist/core/repo/paths.js.map +1 -0
  723. package/dist/core/repo/repo.js +248 -0
  724. package/dist/core/repo/repo.js.map +1 -0
  725. package/dist/core/repo/service-target.js +153 -0
  726. package/dist/core/repo/service-target.js.map +1 -0
  727. package/dist/core/repo/state.js +58 -0
  728. package/dist/core/repo/state.js.map +1 -0
  729. package/dist/core/repo/tree/find.js +52 -0
  730. package/dist/core/repo/tree/find.js.map +1 -0
  731. package/dist/core/repo/tree/fs.js +64 -0
  732. package/dist/core/repo/tree/fs.js.map +1 -0
  733. package/dist/core/repo/tree/marker.js +61 -0
  734. package/dist/core/repo/tree/marker.js.map +1 -0
  735. package/dist/core/repo/tree/views.js +172 -0
  736. package/dist/core/repo/tree/views.js.map +1 -0
  737. package/dist/core/repo/tree/walk.js +234 -0
  738. package/dist/core/repo/tree/walk.js.map +1 -0
  739. package/dist/core/results.js +99 -2
  740. package/dist/core/results.js.map +1 -1
  741. package/dist/core/scaffold/landscape.js +131 -0
  742. package/dist/core/scaffold/landscape.js.map +1 -0
  743. package/dist/core/scaffold/readme.js +84 -0
  744. package/dist/core/scaffold/readme.js.map +1 -0
  745. package/dist/core/staging/commit.js +241 -0
  746. package/dist/core/staging/commit.js.map +1 -0
  747. package/dist/core/staging/interrupted.js +96 -0
  748. package/dist/core/staging/interrupted.js.map +1 -0
  749. package/dist/core/staging/lock.js +206 -0
  750. package/dist/core/staging/lock.js.map +1 -0
  751. package/dist/core/staging/recovery/finding.js +35 -0
  752. package/dist/core/staging/recovery/finding.js.map +1 -0
  753. package/dist/core/staging/recovery/intent.js +98 -0
  754. package/dist/core/staging/recovery/intent.js.map +1 -0
  755. package/dist/core/staging/recovery/recover.js +126 -0
  756. package/dist/core/staging/recovery/recover.js.map +1 -0
  757. package/dist/core/staging/recovery/residue.js +62 -0
  758. package/dist/core/staging/recovery/residue.js.map +1 -0
  759. package/dist/core/staging/snapshot.js +202 -0
  760. package/dist/core/staging/snapshot.js.map +1 -0
  761. package/dist/core/staging/txn/forward.js +171 -0
  762. package/dist/core/staging/txn/forward.js.map +1 -0
  763. package/dist/core/staging/txn/journal.js +139 -0
  764. package/dist/core/staging/txn/journal.js.map +1 -0
  765. package/dist/core/staging/txn/transaction.js +75 -0
  766. package/dist/core/staging/txn/transaction.js.map +1 -0
  767. package/dist/core/staging/writes.js +113 -0
  768. package/dist/core/staging/writes.js.map +1 -0
  769. package/dist/core/status/actions/execution.js +49 -0
  770. package/dist/core/status/actions/execution.js.map +1 -0
  771. package/dist/core/status/contracts.js +102 -0
  772. package/dist/core/status/contracts.js.map +1 -0
  773. package/dist/core/status/feature/artifacts.js +171 -0
  774. package/dist/core/status/feature/artifacts.js.map +1 -0
  775. package/dist/core/status/feature/feature.js +207 -0
  776. package/dist/core/status/feature/feature.js.map +1 -0
  777. package/dist/core/status/feature/next.js +150 -0
  778. package/dist/core/status/feature/next.js.map +1 -0
  779. package/dist/core/status/feature/state.js +21 -0
  780. package/dist/core/status/feature/state.js.map +1 -0
  781. package/dist/core/status/fleet/fleet.js +187 -0
  782. package/dist/core/status/fleet/fleet.js.map +1 -0
  783. package/dist/core/status/fleet/next.js +207 -0
  784. package/dist/core/status/fleet/next.js.map +1 -0
  785. package/dist/core/status/interrupted.js +97 -0
  786. package/dist/core/status/interrupted.js.map +1 -0
  787. package/dist/core/status/report.js +31 -0
  788. package/dist/core/status/report.js.map +1 -0
  789. package/dist/core/status/scan.js +41 -0
  790. package/dist/core/status/scan.js.map +1 -0
  791. package/dist/core/status/verification.js +201 -0
  792. package/dist/core/status/verification.js.map +1 -0
  793. package/dist/core/usecases/capability.js +169 -0
  794. package/dist/core/usecases/capability.js.map +1 -0
  795. package/dist/core/usecases/delta/claims.js +116 -0
  796. package/dist/core/usecases/delta/claims.js.map +1 -0
  797. package/dist/core/usecases/delta/flows.js +103 -0
  798. package/dist/core/usecases/delta/flows.js.map +1 -0
  799. package/dist/core/usecases/delta/overlay.js +235 -0
  800. package/dist/core/usecases/delta/overlay.js.map +1 -0
  801. package/dist/core/usecases/fleet.js +137 -0
  802. package/dist/core/usecases/fleet.js.map +1 -0
  803. package/dist/core/usecases/operations.js +111 -0
  804. package/dist/core/usecases/operations.js.map +1 -0
  805. package/dist/core/usecases/place.js +44 -0
  806. package/dist/core/usecases/place.js.map +1 -0
  807. package/dist/core/usecases/touch.js +101 -0
  808. package/dist/core/usecases/touch.js.map +1 -0
  809. package/dist/core/verify/answers.js +173 -0
  810. package/dist/core/verify/answers.js.map +1 -0
  811. package/dist/core/verify/build.js +150 -0
  812. package/dist/core/verify/build.js.map +1 -0
  813. package/dist/core/verify/checklist.js +201 -0
  814. package/dist/core/verify/checklist.js.map +1 -0
  815. package/dist/core/verify/claims/identity.js +61 -0
  816. package/dist/core/verify/claims/identity.js.map +1 -0
  817. package/dist/core/verify/cross/diff.js +97 -0
  818. package/dist/core/verify/cross/diff.js.map +1 -0
  819. package/dist/core/verify/evidence/contract.js +169 -0
  820. package/dist/core/verify/evidence/contract.js.map +1 -0
  821. package/dist/core/verify/file.js +257 -0
  822. package/dist/core/verify/file.js.map +1 -0
  823. package/dist/core/verify/pins/lint.js +157 -0
  824. package/dist/core/verify/pins/lint.js.map +1 -0
  825. package/dist/core/verify/pins/pin.js +95 -0
  826. package/dist/core/verify/pins/pin.js.map +1 -0
  827. package/dist/core/verify/record.js +142 -0
  828. package/dist/core/verify/record.js.map +1 -0
  829. package/dist/core/verify/store/commit.js +89 -0
  830. package/dist/core/verify/store/commit.js.map +1 -0
  831. package/dist/core/verify/store/render.js +57 -0
  832. package/dist/core/verify/store/render.js.map +1 -0
  833. package/dist/core/vocabulary/health.js +91 -0
  834. package/dist/core/vocabulary/health.js.map +1 -0
  835. package/dist/core/vocabulary/issue.js +55 -0
  836. package/dist/core/vocabulary/issue.js.map +1 -0
  837. package/dist/core/vocabulary/maturity.js +145 -0
  838. package/dist/core/vocabulary/maturity.js.map +1 -0
  839. package/dist/core/{report.js → vocabulary/report.js} +30 -2
  840. package/dist/core/vocabulary/report.js.map +1 -0
  841. package/dist/core/vocabulary/steps.js +266 -0
  842. package/dist/core/vocabulary/steps.js.map +1 -0
  843. package/dist/core/workspace/discover.js +239 -0
  844. package/dist/core/workspace/discover.js.map +1 -0
  845. package/dist/core/workspace/render.js +52 -0
  846. package/dist/core/workspace/render.js.map +1 -0
  847. package/examples/README.md +189 -0
  848. package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
  849. package/examples/docs/architecture/capabilities.yaml +31 -0
  850. package/examples/docs/architecture/landscape.likec4 +280 -0
  851. package/examples/docs/architecture/obligations.yaml +23 -0
  852. package/examples/docs/architecture/permissions.yaml +50 -0
  853. package/examples/docs/architecture/subsystems.likec4 +11 -0
  854. package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
  855. package/examples/docs/capabilities/checkout/spec.md +55 -0
  856. package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
  857. package/examples/docs/capabilities/order-notifications/spec.md +43 -0
  858. package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
  859. package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
  860. package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
  861. package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
  862. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
  863. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
  864. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
  865. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
  866. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
  867. package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
  868. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
  869. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
  870. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
  871. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
  872. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
  873. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
  874. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
  875. package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
  876. package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
  877. package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
  878. package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
  879. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
  880. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
  881. package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
  882. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
  883. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
  884. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
  885. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
  886. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
  887. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
  888. package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
  889. package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
  890. package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
  891. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
  892. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
  893. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
  894. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
  895. package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
  896. package/examples/docs/glossary/order.md +15 -0
  897. package/examples/docs/glossary/payments/authorization.md +14 -0
  898. package/examples/docs/likec4.config.json +9 -0
  899. package/examples/docs/loam.json +3 -0
  900. package/examples/docs/services/checkout-web/model.likec4 +109 -0
  901. package/examples/docs/services/checkout-web/spec.md +91 -0
  902. package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
  903. package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
  904. package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
  905. package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
  906. package/examples/docs/services/order-service/arch.spec.md +127 -0
  907. package/examples/docs/services/order-service/asyncapi.yaml +108 -0
  908. package/examples/docs/services/order-service/health.yaml +57 -0
  909. package/examples/docs/services/order-service/model.likec4 +138 -0
  910. package/examples/docs/services/order-service/openapi.yaml +257 -0
  911. package/examples/docs/services/order-service/runbook.md +62 -0
  912. package/examples/docs/services/order-service/spec.md +134 -0
  913. package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
  914. package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
  915. package/examples/docs/services/payment-service/arch.spec.md +73 -0
  916. package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
  917. package/examples/docs/services/payment-service/health.yaml +39 -0
  918. package/examples/docs/services/payment-service/model.likec4 +67 -0
  919. package/examples/docs/services/payment-service/openapi.yaml +151 -0
  920. package/examples/docs/services/payment-service/runbook.md +37 -0
  921. package/examples/docs/services/payment-service/spec.md +88 -0
  922. package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
  923. package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
  924. package/examples/docs/services/platform/identity-service/health.yaml +70 -0
  925. package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
  926. package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
  927. package/examples/docs/services/platform/identity-service/runbook.md +95 -0
  928. package/examples/docs/services/platform/identity-service/spec.md +119 -0
  929. package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
  930. package/examples/docs/services/platform/notification-service/health.yaml +35 -0
  931. package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
  932. package/examples/docs/services/platform/notification-service/runbook.md +48 -0
  933. package/examples/docs/services/platform/notification-service/spec.md +112 -0
  934. package/examples/docs/services/platform/subsystem.yaml +2 -0
  935. package/examples/fleet.yaml +57 -0
  936. package/package.json +19 -6
  937. package/dist/commands/adopt.js +0 -240
  938. package/dist/commands/adopt.js.map +0 -1
  939. package/dist/commands/archive.js +0 -1313
  940. package/dist/commands/archive.js.map +0 -1
  941. package/dist/commands/delta.js +0 -388
  942. package/dist/commands/delta.js.map +0 -1
  943. package/dist/commands/docs-repo-gate.js.map +0 -1
  944. package/dist/commands/format.js +0 -17
  945. package/dist/commands/format.js.map +0 -1
  946. package/dist/commands/gherkin.js +0 -309
  947. package/dist/commands/gherkin.js.map +0 -1
  948. package/dist/commands/init.js.map +0 -1
  949. package/dist/commands/list.js +0 -295
  950. package/dist/commands/list.js.map +0 -1
  951. package/dist/commands/migrate-openspec.js +0 -950
  952. package/dist/commands/migrate-openspec.js.map +0 -1
  953. package/dist/commands/new.js +0 -413
  954. package/dist/commands/new.js.map +0 -1
  955. package/dist/commands/rebase.js +0 -356
  956. package/dist/commands/rebase.js.map +0 -1
  957. package/dist/commands/show.js.map +0 -1
  958. package/dist/commands/status.js +0 -262
  959. package/dist/commands/status.js.map +0 -1
  960. package/dist/commands/unarchive.js.map +0 -1
  961. package/dist/commands/validate.js +0 -1773
  962. package/dist/commands/validate.js.map +0 -1
  963. package/dist/commands/verify.js +0 -775
  964. package/dist/commands/verify.js.map +0 -1
  965. package/dist/commands/vouch.js +0 -421
  966. package/dist/commands/vouch.js.map +0 -1
  967. package/dist/core/agent.js +0 -2173
  968. package/dist/core/agent.js.map +0 -1
  969. package/dist/core/agents-stamp.js.map +0 -1
  970. package/dist/core/arch.js +0 -130
  971. package/dist/core/arch.js.map +0 -1
  972. package/dist/core/asyncapi.js.map +0 -1
  973. package/dist/core/brief.js +0 -755
  974. package/dist/core/brief.js.map +0 -1
  975. package/dist/core/coherence.js +0 -577
  976. package/dist/core/coherence.js.map +0 -1
  977. package/dist/core/concurrency.js.map +0 -1
  978. package/dist/core/config.js.map +0 -1
  979. package/dist/core/delta.js +0 -476
  980. package/dist/core/delta.js.map +0 -1
  981. package/dist/core/dependencies.js +0 -356
  982. package/dist/core/dependencies.js.map +0 -1
  983. package/dist/core/doctor.js +0 -596
  984. package/dist/core/doctor.js.map +0 -1
  985. package/dist/core/document-bytes.js.map +0 -1
  986. package/dist/core/explore.js.map +0 -1
  987. package/dist/core/frontmatter.js.map +0 -1
  988. package/dist/core/gherkin.js +0 -463
  989. package/dist/core/gherkin.js.map +0 -1
  990. package/dist/core/health.js +0 -67
  991. package/dist/core/health.js.map +0 -1
  992. package/dist/core/ids.js +0 -122
  993. package/dist/core/ids.js.map +0 -1
  994. package/dist/core/issue.js +0 -9
  995. package/dist/core/issue.js.map +0 -1
  996. package/dist/core/json.js +0 -74
  997. package/dist/core/json.js.map +0 -1
  998. package/dist/core/likec4.js +0 -489
  999. package/dist/core/likec4.js.map +0 -1
  1000. package/dist/core/maturity.js +0 -66
  1001. package/dist/core/maturity.js.map +0 -1
  1002. package/dist/core/openapi-merge.js +0 -552
  1003. package/dist/core/openapi-merge.js.map +0 -1
  1004. package/dist/core/openapi.js.map +0 -1
  1005. package/dist/core/openspec-inventory.js +0 -1440
  1006. package/dist/core/openspec-inventory.js.map +0 -1
  1007. package/dist/core/path-safety.js.map +0 -1
  1008. package/dist/core/provenance.js +0 -787
  1009. package/dist/core/provenance.js.map +0 -1
  1010. package/dist/core/records.js.map +0 -1
  1011. package/dist/core/repo.js +0 -438
  1012. package/dist/core/repo.js.map +0 -1
  1013. package/dist/core/report.js.map +0 -1
  1014. package/dist/core/spec.js +0 -505
  1015. package/dist/core/spec.js.map +0 -1
  1016. package/dist/core/staging.js +0 -890
  1017. package/dist/core/staging.js.map +0 -1
  1018. package/dist/core/status.js +0 -1132
  1019. package/dist/core/status.js.map +0 -1
  1020. package/dist/core/steps.js +0 -72
  1021. package/dist/core/steps.js.map +0 -1
  1022. package/dist/core/verify.js +0 -807
  1023. package/dist/core/verify.js.map +0 -1
  1024. package/dist/core/version.js.map +0 -1
  1025. /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
  1026. /package/dist/core/{records.js → kernel/records.js} +0 -0
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Which `services/<id>/` directory a modelled element belongs to.
3
+ *
4
+ * A distinct subject from loading a document, and a distinct phase: the
5
+ * loader beside this package (`../likec4.ts`) turns LikeC4's parse output into
6
+ * loam's neutral `Elem`/`Rel` records, and everything here runs AFTER that, over
7
+ * those records, answering one question the model itself never states outright.
8
+ * It is the join every fleet-wide check rests on — the landscape↔services
9
+ * reconciliation, the spine's edge grouping, `Covers:` resolution, health.yaml's
10
+ * dependency matching — which is why `docs/DESIGN.md`'s Open-decisions table
11
+ * audits its call sites as a subject of their own rather than as a detail of the
12
+ * loader.
13
+ *
14
+ * The resolution order below is the whole of it, and its last rung is the one
15
+ * that lies: read that comment before adding a caller.
16
+ */
17
+ import { declaredService } from "../../kernel/ids/service.js";
18
+ import {} from "../likec4.js";
19
+ /**
20
+ * The service directory an element stands for: an explicit
21
+ * `metadata { service '<id>' }` binding wins, the title is the fallback.
22
+ *
23
+ * The fallback is what every docs repo written before the binding existed relies
24
+ * on, and it is also the trap the binding exists to close: matching on the title
25
+ * means renaming a box in a diagram silently unlinks it from its service, and
26
+ * every check that joined the two just stops finding anything.
27
+ */
28
+ export function elementService(e) {
29
+ return e.service ?? declaredService(e.title);
30
+ }
31
+ /**
32
+ * Every id an endpoint may be filed under, nearest first: `a.b.c`, `a.b`, `a`.
33
+ *
34
+ * A landscape does not have to draw a service as one opaque box. The moment
35
+ * somebody models its containers — `paymentService.api`, `paymentService.worker`
36
+ * — an edge drawn INTO a container is still an edge into the service, and every
37
+ * join that groups by service has to know that. Resolving only the exact id is
38
+ * what made those edges invisible: the spine check silently skipped them, and
39
+ * the no-openapi grace treated a service with a dozen inbound container calls as
40
+ * one nobody calls at all.
41
+ */
42
+ function ancestorIds(id) {
43
+ const out = [id];
44
+ for (let dot = id.lastIndexOf("."); dot !== -1; dot = id.lastIndexOf(".", dot - 1)) {
45
+ out.push(id.slice(0, dot));
46
+ }
47
+ return out;
48
+ }
49
+ /**
50
+ * A memoized `id -> service` resolver over one document.
51
+ *
52
+ * Resolution order, and why it is this order:
53
+ *
54
+ * 1. the nearest ancestor (the id itself first) carrying an explicit
55
+ * `metadata { service '<id>' }` — a binding is a claim somebody wrote down,
56
+ * so it outranks every guess;
57
+ * 2. the nearest ancestor whose title names a REAL `services/<id>/` directory —
58
+ * positive evidence from the filesystem, available only to callers that
59
+ * hand in `known`;
60
+ * 3. the element's own title, and finally the raw id — today's fallback, kept
61
+ * LAST because it is the one that lies: it happily resolves
62
+ * `paymentService.api` to "api", a service that has never existed.
63
+ *
64
+ * Built once per document and shared, because it is called inside loops over
65
+ * every relationship and the walk up the ancestor chain is not free.
66
+ */
67
+ export function serviceResolver(elements, known) {
68
+ const byId = new Map(elements.map((e) => [e.id, e]));
69
+ const memo = new Map();
70
+ return (id) => {
71
+ const hit = memo.get(id);
72
+ if (hit !== undefined)
73
+ return hit;
74
+ let answer;
75
+ for (const candidate of ancestorIds(id)) {
76
+ const e = byId.get(candidate);
77
+ if (e?.service !== undefined) {
78
+ answer = e.service;
79
+ break;
80
+ }
81
+ }
82
+ if (answer === undefined && known !== undefined) {
83
+ for (const candidate of ancestorIds(id)) {
84
+ const e = byId.get(candidate);
85
+ if (e !== undefined && known.has(e.title)) {
86
+ answer = declaredService(e.title);
87
+ break;
88
+ }
89
+ }
90
+ }
91
+ if (answer === undefined) {
92
+ const self = byId.get(id);
93
+ answer = self ? elementService(self) : declaredService(id);
94
+ }
95
+ memo.set(id, answer);
96
+ return answer;
97
+ };
98
+ }
99
+ /**
100
+ * The service a relationship endpoint belongs to. An id that names no element
101
+ * resolves to itself, so a partial document degrades to the id rather than
102
+ * throwing.
103
+ *
104
+ * `known` is the set of service directories that actually exist; pass it
105
+ * wherever the caller has enumerated `services/`, so an edge into a modelled
106
+ * container resolves to the service that owns it instead of to the container's
107
+ * own title. Callers with a single document and no repository in hand omit it
108
+ * and keep the pre-existing behaviour.
109
+ */
110
+ export function serviceOf(elements, id, known) {
111
+ return serviceResolver(elements, known)(id);
112
+ }
113
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../../../src/core/c4/resolve/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,eAAe,EAAwB,MAAM,6BAA6B,CAAC;AACpF,OAAO,EAAa,MAAM,cAAc,CAAC;AAEzC;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,CAAO;IACpC,OAAO,CAAC,CAAC,OAAO,IAAI,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AAC/C,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,WAAW,CAAC,EAAU;IAC7B,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;IACjB,KAAK,IAAI,GAAG,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC;QACnF,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAgB,EAChB,KAA2B;IAE3B,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,MAAM,IAAI,GAAG,IAAI,GAAG,EAA2B,CAAC;IAChD,OAAO,CAAC,EAAU,EAAmB,EAAE;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzB,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,GAAG,CAAC;QAClC,IAAI,MAAmC,CAAC;QACxC,KAAK,MAAM,SAAS,IAAI,WAAW,CAAC,EAAE,CAAC,EAAE,CAAC;YACxC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAC9B,IAAI,CAAC,EAAE,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC7B,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC;gBACnB,MAAM;YACR,CAAC;QACH,CAAC;QACD,IAAI,MAAM,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAChD,KAAK,MAAM,SAAS,IAAI,WAAW,CAAC,EAAE,CAAC,EAAE,CAAC;gBACxC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBAC9B,IAAI,CAAC,KAAK,SAAS,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC1C,MAAM,GAAG,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;oBAClC,MAAM;gBACR,CAAC;YACH,CAAC;QACH,CAAC;QACD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC1B,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACrB,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,EAAU,EAAE,KAA2B;IACjF,OAAO,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;AAC9C,CAAC","sourcesContent":["/**\n * Which `services/<id>/` directory a modelled element belongs to.\n *\n * A distinct subject from loading a document, and a distinct phase: the\n * loader beside this package (`../likec4.ts`) turns LikeC4's parse output into\n * loam's neutral `Elem`/`Rel` records, and everything here runs AFTER that, over\n * those records, answering one question the model itself never states outright.\n * It is the join every fleet-wide check rests on — the landscape↔services\n * reconciliation, the spine's edge grouping, `Covers:` resolution, health.yaml's\n * dependency matching — which is why `docs/DESIGN.md`'s Open-decisions table\n * audits its call sites as a subject of their own rather than as a detail of the\n * loader.\n *\n * The resolution order below is the whole of it, and its last rung is the one\n * that lies: read that comment before adding a caller.\n */\nimport { declaredService, type DeclaredService } from \"../../kernel/ids/service.js\";\nimport { type Elem } from \"../likec4.js\";\n\n/**\n * The service directory an element stands for: an explicit\n * `metadata { service '<id>' }` binding wins, the title is the fallback.\n *\n * The fallback is what every docs repo written before the binding existed relies\n * on, and it is also the trap the binding exists to close: matching on the title\n * means renaming a box in a diagram silently unlinks it from its service, and\n * every check that joined the two just stops finding anything.\n */\nexport function elementService(e: Elem): DeclaredService {\n return e.service ?? declaredService(e.title);\n}\n\n/**\n * Every id an endpoint may be filed under, nearest first: `a.b.c`, `a.b`, `a`.\n *\n * A landscape does not have to draw a service as one opaque box. The moment\n * somebody models its containers — `paymentService.api`, `paymentService.worker`\n * — an edge drawn INTO a container is still an edge into the service, and every\n * join that groups by service has to know that. Resolving only the exact id is\n * what made those edges invisible: the spine check silently skipped them, and\n * the no-openapi grace treated a service with a dozen inbound container calls as\n * one nobody calls at all.\n */\nfunction ancestorIds(id: string): string[] {\n const out = [id];\n for (let dot = id.lastIndexOf(\".\"); dot !== -1; dot = id.lastIndexOf(\".\", dot - 1)) {\n out.push(id.slice(0, dot));\n }\n return out;\n}\n\n/**\n * A memoized `id -> service` resolver over one document.\n *\n * Resolution order, and why it is this order:\n *\n * 1. the nearest ancestor (the id itself first) carrying an explicit\n * `metadata { service '<id>' }` — a binding is a claim somebody wrote down,\n * so it outranks every guess;\n * 2. the nearest ancestor whose title names a REAL `services/<id>/` directory —\n * positive evidence from the filesystem, available only to callers that\n * hand in `known`;\n * 3. the element's own title, and finally the raw id — today's fallback, kept\n * LAST because it is the one that lies: it happily resolves\n * `paymentService.api` to \"api\", a service that has never existed.\n *\n * Built once per document and shared, because it is called inside loops over\n * every relationship and the walk up the ancestor chain is not free.\n */\nexport function serviceResolver(\n elements: Elem[],\n known?: ReadonlySet<string>,\n): (id: string) => DeclaredService {\n const byId = new Map(elements.map((e) => [e.id, e]));\n const memo = new Map<string, DeclaredService>();\n return (id: string): DeclaredService => {\n const hit = memo.get(id);\n if (hit !== undefined) return hit;\n let answer: DeclaredService | undefined;\n for (const candidate of ancestorIds(id)) {\n const e = byId.get(candidate);\n if (e?.service !== undefined) {\n answer = e.service;\n break;\n }\n }\n if (answer === undefined && known !== undefined) {\n for (const candidate of ancestorIds(id)) {\n const e = byId.get(candidate);\n if (e !== undefined && known.has(e.title)) {\n answer = declaredService(e.title);\n break;\n }\n }\n }\n if (answer === undefined) {\n const self = byId.get(id);\n answer = self ? elementService(self) : declaredService(id);\n }\n memo.set(id, answer);\n return answer;\n };\n}\n\n/**\n * The service a relationship endpoint belongs to. An id that names no element\n * resolves to itself, so a partial document degrades to the id rather than\n * throwing.\n *\n * `known` is the set of service directories that actually exist; pass it\n * wherever the caller has enumerated `services/`, so an edge into a modelled\n * container resolves to the service that owns it instead of to the container's\n * own title. Callers with a single document and no repository in hand omit it\n * and keep the pre-existing behaviour.\n */\nexport function serviceOf(elements: Elem[], id: string, known?: ReadonlySet<string>): DeclaredService {\n return serviceResolver(elements, known)(id);\n}\n\n"]}
@@ -0,0 +1,201 @@
1
+ /**
2
+ * fleet.yaml — the tiny human-authored file `loam seed` templates the fleet
3
+ * map from. A human states the facts (which services exist, who calls whom);
4
+ * loam does mechanical templating and guesses nothing, which is what keeps
5
+ * seed on the same side of the no-extractor line as `loam new`.
6
+ *
7
+ * Parsed with the AST (`parseDocument` + `LineCounter`) rather than `parse`,
8
+ * deliberately: every refusal names `file:line`, because the file is edited by
9
+ * hand and "something in it is wrong" is not a fix. Fail-closed throughout —
10
+ * an unknown key, a non-string entry, a malformed call all refuse before the
11
+ * caller writes a single byte; a partially-understood fleet file must never
12
+ * become a partially-right landscape. The node-level readers live in
13
+ * `./items.ts`; this module owns what their answers must amount to.
14
+ *
15
+ * The grammars are the shared ones and never a private copy: service ids
16
+ * through `parseServiceId`, subsystem names through `parseSubsystemName`,
17
+ * externals through the same `dirNameHazard` those two are built on (an
18
+ * external never becomes a directory, but calls name services and externals
19
+ * out of ONE namespace, and a name legal as one and illegal as the other
20
+ * would make the duplicate refusal incoherent — `kernel/ids/service.ts`'s own
21
+ * argument, one level up).
22
+ */
23
+ import { isMap, isScalar, LineCounter, parseDocument } from "yaml";
24
+ import { dirNameHazard, parseServiceId } from "../../kernel/ids/service.js";
25
+ import { parseSubsystemName } from "../../kernel/ids/subsystem.js";
26
+ import { at, hint, listOf, serviceEntry, stringOf } from "./items.js";
27
+ const TOP_KEYS = ["services", "subsystems", "externals", "calls"];
28
+ function invalid(message) {
29
+ return { ok: false, problem: { kind: "invalid", message } };
30
+ }
31
+ /** Parse and validate the fleet file's text. Never throws for an expected outcome. */
32
+ export function readFleetFile(text, file) {
33
+ const lines = new LineCounter();
34
+ const doc = parseDocument(text, { lineCounter: lines });
35
+ const ctx = { file, lines };
36
+ const parseError = doc.errors[0];
37
+ if (parseError !== undefined) {
38
+ const line = parseError.linePos?.[0]?.line;
39
+ return invalid(`${file}${line === undefined ? "" : `:${line}`} cannot be read as YAML — ` +
40
+ `${parseError.message.split("\n")[0]}`);
41
+ }
42
+ if (!isMap(doc.contents)) {
43
+ return invalid(`${file} must be a YAML mapping with a \`services:\` list (and optional ` +
44
+ `\`subsystems:\`, \`externals:\`, \`calls:\`) — the whole document is ` +
45
+ `${doc.contents === null ? "empty" : "not a mapping"}.`);
46
+ }
47
+ // The top level, fail-closed: a mistyped key is a whole section silently
48
+ // dropped, which is exactly the partial scaffold this reader must refuse.
49
+ const sections = new Map();
50
+ for (const pair of doc.contents.items) {
51
+ const key = isScalar(pair.key) && typeof pair.key.value === "string" ? pair.key.value : null;
52
+ if (key === null || !TOP_KEYS.includes(key)) {
53
+ const spelled = key ?? String(pair.key);
54
+ return invalid(`${at(ctx, pair.key)} — unknown key '${spelled}': the fleet file's keys are ` +
55
+ `services, subsystems, externals and calls.${hint(spelled, TOP_KEYS)}`);
56
+ }
57
+ sections.set(key, pair.value);
58
+ }
59
+ // One flat namespace over everything a name can be declared as, because a
60
+ // call endpoint must name exactly one thing — the same rule the tree walk
61
+ // enforces over `services/` (`subsystem.name-collision`).
62
+ const declared = new Map();
63
+ const claim = (name, where, role) => {
64
+ const held = declared.get(name);
65
+ if (held !== undefined) {
66
+ return {
67
+ kind: "duplicate",
68
+ message: `${where} declares '${name}' as ${role}, but ${held.where} already declares it as ` +
69
+ `${held.role} — service ids, subsystem names and externals share one flat namespace ` +
70
+ `(a call endpoint must name exactly one thing). Rename or remove one, then re-run.`,
71
+ };
72
+ }
73
+ declared.set(name, { where, role });
74
+ return null;
75
+ };
76
+ const subsystems = [];
77
+ for (const item of listOf(sections.get("subsystems"))) {
78
+ const raw = stringOf(item);
79
+ if (raw === null)
80
+ return invalid(`${at(ctx, item)} — a subsystems entry must be a string (quote it if YAML reads it as something else).`);
81
+ const parsed = parseSubsystemName(raw, "subsystems entry");
82
+ if (!parsed.ok)
83
+ return invalid(`${at(ctx, item)} — ${parsed.problem}`);
84
+ const dup = claim(raw, at(ctx, item), "a subsystem");
85
+ if (dup !== null)
86
+ return { ok: false, problem: dup };
87
+ subsystems.push(parsed.name);
88
+ }
89
+ const services = [];
90
+ /** Deferred so `subsystem: X` can reference a subsystem declared lower in the file. */
91
+ const subsystemRefs = [];
92
+ for (const item of listOf(sections.get("services"))) {
93
+ const entry = serviceEntry(ctx, item);
94
+ if ("invalid" in entry)
95
+ return invalid(entry.invalid);
96
+ const parsed = parseServiceId(entry.id, "services entry");
97
+ if (!parsed.ok)
98
+ return invalid(`${entry.where} — ${parsed.problem}`);
99
+ const dup = claim(entry.id, entry.where, "a service");
100
+ if (dup !== null)
101
+ return { ok: false, problem: dup };
102
+ if (entry.subsystem !== null)
103
+ subsystemRefs.push({ id: parsed.id, sub: entry.subsystem, where: entry.where });
104
+ services.push({ id: parsed.id, subsystem: null });
105
+ }
106
+ if (services.length === 0) {
107
+ return invalid(`${file} declares no services — \`services:\` is the one required key: list every ` +
108
+ `service id the fleet has (a bare string, or \`{ id, subsystem }\` to file one under a group).`);
109
+ }
110
+ const externals = [];
111
+ for (const item of listOf(sections.get("externals"))) {
112
+ const raw = stringOf(item);
113
+ if (raw === null)
114
+ return invalid(`${at(ctx, item)} — an externals entry must be a string (quote it if YAML reads it as something else).`);
115
+ if (dirNameHazard(raw) !== null) {
116
+ return invalid(`${at(ctx, item)} — '${raw}' cannot be an external's name: externals share the service-id ` +
117
+ `grammar (start with a letter or digit; letters, digits, '.', '_', '-'; no Windows-reserved ` +
118
+ `names), because calls name services and externals out of one namespace.`);
119
+ }
120
+ const dup = claim(raw, at(ctx, item), "an external");
121
+ if (dup !== null)
122
+ return { ok: false, problem: dup };
123
+ externals.push(raw);
124
+ }
125
+ // Subsystem references, now that every declaration is in: a `subsystem:`
126
+ // naming nothing is the second-likeliest typo in the file, and the hint
127
+ // offers only names the file really declares.
128
+ const subNames = new Set(subsystems);
129
+ for (const ref of subsystemRefs) {
130
+ if (!subNames.has(ref.sub)) {
131
+ return {
132
+ ok: false,
133
+ problem: {
134
+ kind: "unknown-subsystem",
135
+ message: `${ref.where} files service '${ref.id}' under '${ref.sub}', but \`subsystems:\` ` +
136
+ `declares no '${ref.sub}'.${hint(ref.sub, subsystems)} Add it to subsystems:, ` +
137
+ `or fix the spelling, then re-run.`,
138
+ },
139
+ };
140
+ }
141
+ }
142
+ // The branded name comes from the DECLARATION list, never from a second
143
+ // parse of the reference: `subsystems:` is where the grammar ran, and the
144
+ // membership check above proved every reference resolves into it.
145
+ const bySubName = new Map(subsystems.map((s) => [s, s]));
146
+ const filed = new Map(subsystemRefs.map((r) => [r.id, bySubName.get(r.sub) ?? null]));
147
+ const seededServices = services.map((s) => ({
148
+ id: s.id,
149
+ subsystem: filed.get(s.id) ?? null,
150
+ }));
151
+ const endpointProblem = (raw, name, where) => {
152
+ if (subNames.has(name)) {
153
+ return {
154
+ kind: "unknown-endpoint",
155
+ message: `${where} — call '${raw}' names the subsystem '${name}': a subsystem is a place ` +
156
+ `services live, never a call endpoint. Name one of its services instead.`,
157
+ };
158
+ }
159
+ return {
160
+ kind: "unknown-endpoint",
161
+ message: `${where} — call '${raw}' names '${name}', which no services: or externals: entry ` +
162
+ `declares.${hint(name, [...services.map((s) => s.id), ...externals])} Declare it, ` +
163
+ `or fix the spelling, then re-run.`,
164
+ };
165
+ };
166
+ const callable = new Set([...services.map((s) => s.id), ...externals]);
167
+ const calls = [];
168
+ const seenCalls = new Set();
169
+ for (const item of listOf(sections.get("calls"))) {
170
+ const raw = stringOf(item);
171
+ if (raw === null)
172
+ return invalid(`${at(ctx, item)} — a calls entry must be a string of the form 'caller -> callee'.`);
173
+ const m = /^(\S+)\s*->\s*(\S+)$/.exec(raw.trim());
174
+ if (m === null) {
175
+ return invalid(`${at(ctx, item)} — a call is written 'caller -> callee' (got '${raw}').`);
176
+ }
177
+ const [from, to] = [m[1], m[2]];
178
+ for (const end of [from, to]) {
179
+ if (!callable.has(end))
180
+ return { ok: false, problem: endpointProblem(raw, end, at(ctx, item)) };
181
+ }
182
+ // LikeC4 has no self-edge: it reads `a -> a` as a parent-child
183
+ // relationship and refuses the whole file. Caught HERE, with the line,
184
+ // because the alternative is the self-check refusing `internal` over a
185
+ // fleet file whose fault it is not — and a copy-paste inside `calls:` is
186
+ // exactly how this line gets written.
187
+ if (from === to) {
188
+ return invalid(`${at(ctx, item)} — call '${raw}' names '${from}' at both ends, and LikeC4 has no ` +
189
+ `self-edge (it reads 'a -> a' as a parent-child relationship). Name the other ` +
190
+ `endpoint, or drop the line.`);
191
+ }
192
+ const key = `${from} ${to}`;
193
+ // A repeated line is the same fact stated twice, not a second edge.
194
+ if (seenCalls.has(key))
195
+ continue;
196
+ seenCalls.add(key);
197
+ calls.push({ from, to });
198
+ }
199
+ return { ok: true, seed: { services: seededServices, subsystems, externals, calls } };
200
+ }
201
+ //# sourceMappingURL=fleet-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fleet-file.js","sourceRoot":"","sources":["../../../../src/core/c4/seed/fleet-file.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * fleet.yaml — the tiny human-authored file `loam seed` templates the fleet\n * map from. A human states the facts (which services exist, who calls whom);\n * loam does mechanical templating and guesses nothing, which is what keeps\n * seed on the same side of the no-extractor line as `loam new`.\n *\n * Parsed with the AST (`parseDocument` + `LineCounter`) rather than `parse`,\n * deliberately: every refusal names `file:line`, because the file is edited by\n * hand and \"something in it is wrong\" is not a fix. Fail-closed throughout —\n * an unknown key, a non-string entry, a malformed call all refuse before the\n * caller writes a single byte; a partially-understood fleet file must never\n * become a partially-right landscape. The node-level readers live in\n * `./items.ts`; this module owns what their answers must amount to.\n *\n * The grammars are the shared ones and never a private copy: service ids\n * through `parseServiceId`, subsystem names through `parseSubsystemName`,\n * externals through the same `dirNameHazard` those two are built on (an\n * external never becomes a directory, but calls name services and externals\n * out of ONE namespace, and a name legal as one and illegal as the other\n * would make the duplicate refusal incoherent — `kernel/ids/service.ts`'s own\n * argument, one level up).\n */\nimport { isMap, isScalar, LineCounter, parseDocument, type Pair } from \"yaml\";\nimport { dirNameHazard, parseServiceId, type ServiceId } from \"../../kernel/ids/service.js\";\nimport { parseSubsystemName, type SubsystemName } from \"../../kernel/ids/subsystem.js\";\nimport { at, hint, listOf, serviceEntry, stringOf, type SeedCtx } from \"./items.js\";\n\nexport interface SeedService {\n readonly id: ServiceId;\n /** The subsystem this service is filed under, or null for unfiled. */\n readonly subsystem: SubsystemName | null;\n}\n\n/** One `a -> b` line. Endpoints are declared names (service ids or externals), not element ids. */\nexport interface SeedCall {\n readonly from: string;\n readonly to: string;\n}\n\n/** The whole fleet file, validated: what the templater renders from. */\nexport interface FleetSeed {\n readonly services: readonly SeedService[];\n readonly subsystems: readonly SubsystemName[];\n readonly externals: readonly string[];\n readonly calls: readonly SeedCall[];\n}\n\n/**\n * Why the file was refused, discriminated by the ACTION the caller takes —\n * each kind maps to one stable error code at the command layer (`invalid` →\n * `seed-file-invalid`, `duplicate` → `seed-duplicate-service`,\n * `unknown-subsystem` → `seed-unknown-subsystem`, `unknown-endpoint` → the\n * existing `unknown-service`, because the fix is the same as for every other\n * unknown service: correct the name and re-run).\n */\nexport type SeedFileProblem =\n | { readonly kind: \"invalid\"; readonly message: string }\n | { readonly kind: \"duplicate\"; readonly message: string }\n | { readonly kind: \"unknown-subsystem\"; readonly message: string }\n | { readonly kind: \"unknown-endpoint\"; readonly message: string };\n\nexport type FleetFileRead =\n | { readonly ok: true; readonly seed: FleetSeed }\n | { readonly ok: false; readonly problem: SeedFileProblem };\n\nconst TOP_KEYS = [\"services\", \"subsystems\", \"externals\", \"calls\"] as const;\n\nfunction invalid(message: string): FleetFileRead {\n return { ok: false, problem: { kind: \"invalid\", message } };\n}\n\n/** Parse and validate the fleet file's text. Never throws for an expected outcome. */\nexport function readFleetFile(text: string, file: string): FleetFileRead {\n const lines = new LineCounter();\n const doc = parseDocument(text, { lineCounter: lines });\n const ctx: SeedCtx = { file, lines };\n const parseError = doc.errors[0];\n if (parseError !== undefined) {\n const line = parseError.linePos?.[0]?.line;\n return invalid(\n `${file}${line === undefined ? \"\" : `:${line}`} cannot be read as YAML — ` +\n `${parseError.message.split(\"\\n\")[0]!}`,\n );\n }\n if (!isMap(doc.contents)) {\n return invalid(\n `${file} must be a YAML mapping with a \\`services:\\` list (and optional ` +\n `\\`subsystems:\\`, \\`externals:\\`, \\`calls:\\`) — the whole document is ` +\n `${doc.contents === null ? \"empty\" : \"not a mapping\"}.`,\n );\n }\n\n // The top level, fail-closed: a mistyped key is a whole section silently\n // dropped, which is exactly the partial scaffold this reader must refuse.\n const sections = new Map<string, unknown>();\n for (const pair of doc.contents.items as Pair[]) {\n const key = isScalar(pair.key) && typeof pair.key.value === \"string\" ? pair.key.value : null;\n if (key === null || !(TOP_KEYS as readonly string[]).includes(key)) {\n const spelled = key ?? String(pair.key);\n return invalid(\n `${at(ctx, pair.key)} — unknown key '${spelled}': the fleet file's keys are ` +\n `services, subsystems, externals and calls.${hint(spelled, TOP_KEYS)}`,\n );\n }\n sections.set(key, pair.value);\n }\n\n // One flat namespace over everything a name can be declared as, because a\n // call endpoint must name exactly one thing — the same rule the tree walk\n // enforces over `services/` (`subsystem.name-collision`).\n const declared = new Map<string, { where: string; role: string }>();\n const claim = (name: string, where: string, role: string): SeedFileProblem | null => {\n const held = declared.get(name);\n if (held !== undefined) {\n return {\n kind: \"duplicate\",\n message:\n `${where} declares '${name}' as ${role}, but ${held.where} already declares it as ` +\n `${held.role} — service ids, subsystem names and externals share one flat namespace ` +\n `(a call endpoint must name exactly one thing). Rename or remove one, then re-run.`,\n };\n }\n declared.set(name, { where, role });\n return null;\n };\n\n const subsystems: SubsystemName[] = [];\n for (const item of listOf(sections.get(\"subsystems\"))) {\n const raw = stringOf(item);\n if (raw === null) return invalid(`${at(ctx, item)} — a subsystems entry must be a string (quote it if YAML reads it as something else).`);\n const parsed = parseSubsystemName(raw, \"subsystems entry\");\n if (!parsed.ok) return invalid(`${at(ctx, item)} — ${parsed.problem}`);\n const dup = claim(raw, at(ctx, item), \"a subsystem\");\n if (dup !== null) return { ok: false, problem: dup };\n subsystems.push(parsed.name);\n }\n\n const services: SeedService[] = [];\n /** Deferred so `subsystem: X` can reference a subsystem declared lower in the file. */\n const subsystemRefs: { id: ServiceId; sub: string; where: string }[] = [];\n for (const item of listOf(sections.get(\"services\"))) {\n const entry = serviceEntry(ctx, item);\n if (\"invalid\" in entry) return invalid(entry.invalid);\n const parsed = parseServiceId(entry.id, \"services entry\");\n if (!parsed.ok) return invalid(`${entry.where} — ${parsed.problem}`);\n const dup = claim(entry.id, entry.where, \"a service\");\n if (dup !== null) return { ok: false, problem: dup };\n if (entry.subsystem !== null) subsystemRefs.push({ id: parsed.id, sub: entry.subsystem, where: entry.where });\n services.push({ id: parsed.id, subsystem: null });\n }\n if (services.length === 0) {\n return invalid(\n `${file} declares no services — \\`services:\\` is the one required key: list every ` +\n `service id the fleet has (a bare string, or \\`{ id, subsystem }\\` to file one under a group).`,\n );\n }\n\n const externals: string[] = [];\n for (const item of listOf(sections.get(\"externals\"))) {\n const raw = stringOf(item);\n if (raw === null) return invalid(`${at(ctx, item)} — an externals entry must be a string (quote it if YAML reads it as something else).`);\n if (dirNameHazard(raw) !== null) {\n return invalid(\n `${at(ctx, item)} — '${raw}' cannot be an external's name: externals share the service-id ` +\n `grammar (start with a letter or digit; letters, digits, '.', '_', '-'; no Windows-reserved ` +\n `names), because calls name services and externals out of one namespace.`,\n );\n }\n const dup = claim(raw, at(ctx, item), \"an external\");\n if (dup !== null) return { ok: false, problem: dup };\n externals.push(raw);\n }\n\n // Subsystem references, now that every declaration is in: a `subsystem:`\n // naming nothing is the second-likeliest typo in the file, and the hint\n // offers only names the file really declares.\n const subNames = new Set<string>(subsystems);\n for (const ref of subsystemRefs) {\n if (!subNames.has(ref.sub)) {\n return {\n ok: false,\n problem: {\n kind: \"unknown-subsystem\",\n message:\n `${ref.where} files service '${ref.id}' under '${ref.sub}', but \\`subsystems:\\` ` +\n `declares no '${ref.sub}'.${hint(ref.sub, subsystems)} Add it to subsystems:, ` +\n `or fix the spelling, then re-run.`,\n },\n };\n }\n }\n // The branded name comes from the DECLARATION list, never from a second\n // parse of the reference: `subsystems:` is where the grammar ran, and the\n // membership check above proved every reference resolves into it.\n const bySubName = new Map<string, SubsystemName>(subsystems.map((s) => [s as string, s]));\n const filed = new Map(subsystemRefs.map((r) => [r.id as string, bySubName.get(r.sub) ?? null]));\n const seededServices: SeedService[] = services.map((s) => ({\n id: s.id,\n subsystem: filed.get(s.id) ?? null,\n }));\n\n const endpointProblem = (raw: string, name: string, where: string): SeedFileProblem => {\n if (subNames.has(name)) {\n return {\n kind: \"unknown-endpoint\",\n message:\n `${where} — call '${raw}' names the subsystem '${name}': a subsystem is a place ` +\n `services live, never a call endpoint. Name one of its services instead.`,\n };\n }\n return {\n kind: \"unknown-endpoint\",\n message:\n `${where} — call '${raw}' names '${name}', which no services: or externals: entry ` +\n `declares.${hint(name, [...services.map((s) => s.id), ...externals])} Declare it, ` +\n `or fix the spelling, then re-run.`,\n };\n };\n const callable = new Set<string>([...services.map((s) => s.id), ...externals]);\n const calls: SeedCall[] = [];\n const seenCalls = new Set<string>();\n for (const item of listOf(sections.get(\"calls\"))) {\n const raw = stringOf(item);\n if (raw === null) return invalid(`${at(ctx, item)} — a calls entry must be a string of the form 'caller -> callee'.`);\n const m = /^(\\S+)\\s*->\\s*(\\S+)$/.exec(raw.trim());\n if (m === null) {\n return invalid(`${at(ctx, item)} — a call is written 'caller -> callee' (got '${raw}').`);\n }\n const [from, to] = [m[1]!, m[2]!];\n for (const end of [from, to]) {\n if (!callable.has(end)) return { ok: false, problem: endpointProblem(raw, end, at(ctx, item)) };\n }\n // LikeC4 has no self-edge: it reads `a -> a` as a parent-child\n // relationship and refuses the whole file. Caught HERE, with the line,\n // because the alternative is the self-check refusing `internal` over a\n // fleet file whose fault it is not — and a copy-paste inside `calls:` is\n // exactly how this line gets written.\n if (from === to) {\n return invalid(\n `${at(ctx, item)} — call '${raw}' names '${from}' at both ends, and LikeC4 has no ` +\n `self-edge (it reads 'a -> a' as a parent-child relationship). Name the other ` +\n `endpoint, or drop the line.`,\n );\n }\n const key = `${from} ${to}`;\n // A repeated line is the same fact stated twice, not a second edge.\n if (seenCalls.has(key)) continue;\n seenCalls.add(key);\n calls.push({ from, to });\n }\n\n return { ok: true, seed: { services: seededServices, subsystems, externals, calls } };\n}\n"]}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * One fleet.yaml item, read off the AST: the section-item and scalar readers,
3
+ * the `file:line` addressing every refusal opens with, and the one composite
4
+ * entry shape (`services:`'s string-or-mapping item). Split from
5
+ * `./fleet-file.ts` on the phase seam — this module answers "what does this
6
+ * NODE say, and where is it", while fleet-file owns what the answers must
7
+ * amount to (grammar, the flat namespace, reference resolution).
8
+ *
9
+ * `serviceEntry` reports its refusals as a plain `invalid` sentence rather
10
+ * than the reader's discriminated problem union, deliberately: every shape
11
+ * failure at item level maps to `seed-file-invalid`, and returning the
12
+ * sentence keeps this module free of the domain union — the import points
13
+ * fleet-file → items only, never back.
14
+ */
15
+ import { isMap, isScalar, isSeq } from "yaml";
16
+ import { closeIds } from "../arch.js";
17
+ /** `file:line` for a node, or just the file when the node carries no range. */
18
+ export function at(ctx, node) {
19
+ const range = node.range;
20
+ if (range === undefined || range === null)
21
+ return ctx.file;
22
+ return `${ctx.file}:${ctx.lines.linePos(range[0]).line}`;
23
+ }
24
+ /** A did-you-mean clause over real names, or the empty string. */
25
+ export function hint(name, pool) {
26
+ const close = closeIds(name, [...pool]);
27
+ return close.length === 0 ? "" : ` Did you mean ${close.map((c) => `'${c}'`).join(" or ")}?`;
28
+ }
29
+ /**
30
+ * A section's items. An absent key and a key with nothing under it are both
31
+ * the empty list — clearing out the last entry must not require deleting the
32
+ * heading too. A non-sequence value comes back as ONE item, which reads
33
+ * `services: checkout` as the one-entry list it plainly means and hands
34
+ * everything else — a number, a mapping under `calls:` — to the caller's
35
+ * per-item type check, which refuses it with the node's own line.
36
+ */
37
+ export function listOf(node) {
38
+ if (node === undefined || node === null)
39
+ return [];
40
+ // `parseDocument` gives a heading with nothing under it a Scalar HOLDING
41
+ // null, never a bare null, so the emptied-out section only looks like the
42
+ // absent one above. Without this arm, deleting the last entry under
43
+ // `externals:` refuses with "an externals entry must be a string (quote it
44
+ // if YAML reads it as something else)" — advice about a value that is not
45
+ // there — and an emptied `services:` shadows the friendly "declares no
46
+ // services" sentence that exists for exactly that case.
47
+ if (isScalar(node) && node.value === null)
48
+ return [];
49
+ if (isSeq(node))
50
+ return node.items;
51
+ return [node];
52
+ }
53
+ /** A scalar's string value, or null when the entry is not a string. */
54
+ export function stringOf(item) {
55
+ if (!isScalar(item) || typeof item.value !== "string")
56
+ return null;
57
+ return item.value;
58
+ }
59
+ /** One services entry: a bare string id, or `{ id, subsystem? }` — nothing else. */
60
+ export function serviceEntry(ctx, item) {
61
+ const where = at(ctx, item);
62
+ const bare = stringOf(item);
63
+ if (bare !== null)
64
+ return { id: bare, subsystem: null, where };
65
+ if (!isMap(item)) {
66
+ return { invalid: `${where} — a services entry is a string id, or a mapping with \`id\` and optional \`subsystem\`.` };
67
+ }
68
+ let id = null;
69
+ let subsystem = null;
70
+ for (const pair of item.items) {
71
+ const key = isScalar(pair.key) && typeof pair.key.value === "string" ? pair.key.value : null;
72
+ const value = stringOf(pair.value);
73
+ if (key === "id" && value !== null)
74
+ id = value;
75
+ else if (key === "subsystem" && value !== null)
76
+ subsystem = value;
77
+ else {
78
+ const spelled = key ?? String(pair.key);
79
+ return {
80
+ invalid: `${at(ctx, pair.key)} — a services entry takes \`id\` and optional \`subsystem\` ` +
81
+ `(both strings); '${spelled}' is ${key === "id" || key === "subsystem" ? "not a string here" : "not one of them"}.` +
82
+ `${hint(spelled, ["id", "subsystem"])}`,
83
+ };
84
+ }
85
+ }
86
+ if (id === null) {
87
+ return { invalid: `${where} — a services mapping entry must carry \`id\`.` };
88
+ }
89
+ return { id, subsystem, where };
90
+ }
91
+ //# sourceMappingURL=items.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"items.js","sourceRoot":"","sources":["../../../../src/core/c4/seed/items.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One fleet.yaml item, read off the AST: the section-item and scalar readers,\n * the `file:line` addressing every refusal opens with, and the one composite\n * entry shape (`services:`'s string-or-mapping item). Split from\n * `./fleet-file.ts` on the phase seam — this module answers \"what does this\n * NODE say, and where is it\", while fleet-file owns what the answers must\n * amount to (grammar, the flat namespace, reference resolution).\n *\n * `serviceEntry` reports its refusals as a plain `invalid` sentence rather\n * than the reader's discriminated problem union, deliberately: every shape\n * failure at item level maps to `seed-file-invalid`, and returning the\n * sentence keeps this module free of the domain union — the import points\n * fleet-file → items only, never back.\n */\nimport { isMap, isScalar, isSeq, type LineCounter, type Pair } from \"yaml\";\nimport { closeIds } from \"../arch.js\";\n\n/** What the readers walk with: the display path and the offset→line map. */\nexport interface SeedCtx {\n readonly file: string;\n readonly lines: LineCounter;\n}\n\n/** `file:line` for a node, or just the file when the node carries no range. */\nexport function at(ctx: SeedCtx, node: unknown): string {\n const range = (node as { range?: readonly [number, number, number] | null }).range;\n if (range === undefined || range === null) return ctx.file;\n return `${ctx.file}:${ctx.lines.linePos(range[0]).line}`;\n}\n\n/** A did-you-mean clause over real names, or the empty string. */\nexport function hint(name: string, pool: readonly string[]): string {\n const close = closeIds(name, [...pool]);\n return close.length === 0 ? \"\" : ` Did you mean ${close.map((c) => `'${c}'`).join(\" or \")}?`;\n}\n\n/**\n * A section's items. An absent key and a key with nothing under it are both\n * the empty list — clearing out the last entry must not require deleting the\n * heading too. A non-sequence value comes back as ONE item, which reads\n * `services: checkout` as the one-entry list it plainly means and hands\n * everything else — a number, a mapping under `calls:` — to the caller's\n * per-item type check, which refuses it with the node's own line.\n */\nexport function listOf(node: unknown): unknown[] {\n if (node === undefined || node === null) return [];\n // `parseDocument` gives a heading with nothing under it a Scalar HOLDING\n // null, never a bare null, so the emptied-out section only looks like the\n // absent one above. Without this arm, deleting the last entry under\n // `externals:` refuses with \"an externals entry must be a string (quote it\n // if YAML reads it as something else)\" — advice about a value that is not\n // there — and an emptied `services:` shadows the friendly \"declares no\n // services\" sentence that exists for exactly that case.\n if (isScalar(node) && node.value === null) return [];\n if (isSeq(node)) return node.items;\n return [node];\n}\n\n/** A scalar's string value, or null when the entry is not a string. */\nexport function stringOf(item: unknown): string | null {\n if (!isScalar(item) || typeof item.value !== \"string\") return null;\n return item.value;\n}\n\nexport type ServiceEntryRead =\n | { id: string; subsystem: string | null; where: string }\n | { invalid: string };\n\n/** One services entry: a bare string id, or `{ id, subsystem? }` — nothing else. */\nexport function serviceEntry(ctx: SeedCtx, item: unknown): ServiceEntryRead {\n const where = at(ctx, item);\n const bare = stringOf(item);\n if (bare !== null) return { id: bare, subsystem: null, where };\n if (!isMap(item)) {\n return { invalid: `${where} — a services entry is a string id, or a mapping with \\`id\\` and optional \\`subsystem\\`.` };\n }\n let id: string | null = null;\n let subsystem: string | null = null;\n for (const pair of item.items as Pair[]) {\n const key = isScalar(pair.key) && typeof pair.key.value === \"string\" ? pair.key.value : null;\n const value = stringOf(pair.value);\n if (key === \"id\" && value !== null) id = value;\n else if (key === \"subsystem\" && value !== null) subsystem = value;\n else {\n const spelled = key ?? String(pair.key);\n return {\n invalid:\n `${at(ctx, pair.key)} — a services entry takes \\`id\\` and optional \\`subsystem\\` ` +\n `(both strings); '${spelled}' is ${key === \"id\" || key === \"subsystem\" ? \"not a string here\" : \"not one of them\"}.` +\n `${hint(spelled, [\"id\", \"subsystem\"])}`,\n };\n }\n }\n if (id === null) {\n return { invalid: `${where} — a services mapping entry must carry \\`id\\`.` };\n }\n return { id, subsystem, where };\n}\n"]}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The generated-by stamp on a seeded landscape: line 1, self-verifying, no
3
+ * state anywhere else. `loam seed` may regenerate the fleet map wholesale
4
+ * exactly when it can PROVE no human work would be lost, and the only proof
5
+ * that needs no side channel is the file testifying about itself — a digest of
6
+ * every byte after the stamp line, written into the stamp line. A database of
7
+ * "files loam wrote" would be a second source of truth that drifts the first
8
+ * time somebody copies a docs repo; the digest travels with the bytes it
9
+ * covers.
10
+ *
11
+ * The stamp is recognised by its PREFIX and digest alone — the prose after the
12
+ * digest is free to be reworded (it is advice to a reader, not part of the
13
+ * proof), so a future loam rewording the sentence still recognises every file
14
+ * an older loam sealed. The digest covers everything after the first newline,
15
+ * with line endings normalised out: `core.autocrlf` is Git for Windows'
16
+ * installer default and the docs repo ships no `.gitattributes`, so an
17
+ * ordinary Windows clone of a seeded landscape rewrites every line of it and
18
+ * changes not one fact. Calling that "hand-edited" would stand `loam seed`
19
+ * against `loam status`, which reads the same file through
20
+ * `isLandscapeStub`'s CRLF-normalised compare and tells the reader to run
21
+ * seed. What the digest proves is that the CONTENT is still a pure function of
22
+ * fleet.yaml — which is what "no human work here" means.
23
+ */
24
+ import { createHash } from "node:crypto";
25
+ const STAMP_PREFIX = "// loam-seed sha256:";
26
+ /** Line 1 must OPEN with the prefix and a full digest; the prose after is free. */
27
+ const STAMP_LINE = /^\/\/ loam-seed sha256:([0-9a-f]{64})(?:\s|$)/;
28
+ /** The digest the stamp carries: content, with line endings normalised out. See the header. */
29
+ function digestOf(body) {
30
+ return createHash("sha256").update(body.replace(/\r\n/g, "\n"), "utf8").digest("hex");
31
+ }
32
+ /** Prepend the self-verifying stamp line to a rendered landscape body. */
33
+ export function sealLandscape(body) {
34
+ return (`${STAMP_PREFIX}${digestOf(body)} — generated from fleet.yaml by \`loam seed\`. ` +
35
+ "Hand edits make this file yours: seed will then refuse to regenerate it.\n" +
36
+ body);
37
+ }
38
+ /** Classify a landscape's text by its own testimony. See {@link LandscapeProvenance}. */
39
+ export function landscapeProvenance(text) {
40
+ const newline = text.indexOf("\n");
41
+ const first = newline === -1 ? text : text.slice(0, newline);
42
+ const m = STAMP_LINE.exec(first);
43
+ if (m === null)
44
+ return "foreign";
45
+ const body = newline === -1 ? "" : text.slice(newline + 1);
46
+ return digestOf(body) === m[1] ? "seed-stamped" : "seed-edited";
47
+ }
48
+ //# sourceMappingURL=stamp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stamp.js","sourceRoot":"","sources":["../../../../src/core/c4/seed/stamp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAezC,MAAM,YAAY,GAAG,sBAAsB,CAAC;AAE5C,mFAAmF;AACnF,MAAM,UAAU,GAAG,+CAA+C,CAAC;AAEnE,+FAA+F;AAC/F,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACxF,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,CACL,GAAG,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,iDAAiD;QACjF,4EAA4E;QAC5E,IAAI,CACL,CAAC;AACJ,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC7D,MAAM,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjC,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACjC,MAAM,IAAI,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;IAC3D,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,aAAa,CAAC;AAClE,CAAC","sourcesContent":["/**\n * The generated-by stamp on a seeded landscape: line 1, self-verifying, no\n * state anywhere else. `loam seed` may regenerate the fleet map wholesale\n * exactly when it can PROVE no human work would be lost, and the only proof\n * that needs no side channel is the file testifying about itself — a digest of\n * every byte after the stamp line, written into the stamp line. A database of\n * \"files loam wrote\" would be a second source of truth that drifts the first\n * time somebody copies a docs repo; the digest travels with the bytes it\n * covers.\n *\n * The stamp is recognised by its PREFIX and digest alone — the prose after the\n * digest is free to be reworded (it is advice to a reader, not part of the\n * proof), so a future loam rewording the sentence still recognises every file\n * an older loam sealed. The digest covers everything after the first newline,\n * with line endings normalised out: `core.autocrlf` is Git for Windows'\n * installer default and the docs repo ships no `.gitattributes`, so an\n * ordinary Windows clone of a seeded landscape rewrites every line of it and\n * changes not one fact. Calling that \"hand-edited\" would stand `loam seed`\n * against `loam status`, which reads the same file through\n * `isLandscapeStub`'s CRLF-normalised compare and tells the reader to run\n * seed. What the digest proves is that the CONTENT is still a pure function of\n * fleet.yaml — which is what \"no human work here\" means.\n */\nimport { createHash } from \"node:crypto\";\n\n/**\n * What the landscape's bytes say about who wrote them:\n *\n * - `seed-stamped` — the stamp is present and its digest matches: every byte\n * is a pure function of fleet.yaml, so regenerating loses nothing.\n * - `seed-edited` — the stamp is present but the digest does not match: a\n * human edited a seeded file, and those edits are theirs to keep.\n * - `foreign` — no stamp: the file was authored some other way (by hand, or by\n * the scaffold; the caller recognises the scaffold's stub separately, through\n * `isLandscapeStub`, because the stub predates the stamp).\n */\nexport type LandscapeProvenance = \"seed-stamped\" | \"seed-edited\" | \"foreign\";\n\nconst STAMP_PREFIX = \"// loam-seed sha256:\";\n\n/** Line 1 must OPEN with the prefix and a full digest; the prose after is free. */\nconst STAMP_LINE = /^\\/\\/ loam-seed sha256:([0-9a-f]{64})(?:\\s|$)/;\n\n/** The digest the stamp carries: content, with line endings normalised out. See the header. */\nfunction digestOf(body: string): string {\n return createHash(\"sha256\").update(body.replace(/\\r\\n/g, \"\\n\"), \"utf8\").digest(\"hex\");\n}\n\n/** Prepend the self-verifying stamp line to a rendered landscape body. */\nexport function sealLandscape(body: string): string {\n return (\n `${STAMP_PREFIX}${digestOf(body)} — generated from fleet.yaml by \\`loam seed\\`. ` +\n \"Hand edits make this file yours: seed will then refuse to regenerate it.\\n\" +\n body\n );\n}\n\n/** Classify a landscape's text by its own testimony. See {@link LandscapeProvenance}. */\nexport function landscapeProvenance(text: string): LandscapeProvenance {\n const newline = text.indexOf(\"\\n\");\n const first = newline === -1 ? text : text.slice(0, newline);\n const m = STAMP_LINE.exec(first);\n if (m === null) return \"foreign\";\n const body = newline === -1 ? \"\" : text.slice(newline + 1);\n return digestOf(body) === m[1] ? \"seed-stamped\" : \"seed-edited\";\n}\n"]}