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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1036) hide show
  1. package/CHANGELOG.md +2635 -86
  2. package/COMPARISON.md +80 -63
  3. package/CONTRIBUTING.md +84 -0
  4. package/MIGRATING-from-OpenSpec.md +213 -54
  5. package/QUICKSTART.md +125 -0
  6. package/README.md +450 -150
  7. package/ROADMAP.md +599 -0
  8. package/SCHEMA.md +2355 -256
  9. package/SECURITY.md +21 -3
  10. package/WORKFLOW.md +701 -0
  11. package/dist/cli.js +113 -19
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/adopt/adopt.js +209 -0
  14. package/dist/commands/adopt/adopt.js.map +1 -0
  15. package/dist/commands/adopt/render.js +268 -0
  16. package/dist/commands/adopt/render.js.map +1 -0
  17. package/dist/commands/archive/archive.js +77 -0
  18. package/dist/commands/archive/archive.js.map +1 -0
  19. package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
  21. package/dist/commands/archive/plan/contracts/openapi.js +159 -0
  22. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
  23. package/dist/commands/archive/plan/gate.js +323 -0
  24. package/dist/commands/archive/plan/gate.js.map +1 -0
  25. package/dist/commands/archive/plan/landscape.js +180 -0
  26. package/dist/commands/archive/plan/landscape.js.map +1 -0
  27. package/dist/commands/archive/plan/refusal.js +111 -0
  28. package/dist/commands/archive/plan/refusal.js.map +1 -0
  29. package/dist/commands/archive/plan/specs.js +237 -0
  30. package/dist/commands/archive/plan/specs.js.map +1 -0
  31. package/dist/commands/archive/plan/state.js +27 -0
  32. package/dist/commands/archive/plan/state.js.map +1 -0
  33. package/dist/commands/archive/run.js +252 -0
  34. package/dist/commands/archive/run.js.map +1 -0
  35. package/dist/commands/context/context.js +99 -0
  36. package/dist/commands/context/context.js.map +1 -0
  37. package/dist/commands/context/print.js +283 -0
  38. package/dist/commands/context/print.js.map +1 -0
  39. package/dist/commands/delta/delta.js +279 -0
  40. package/dist/commands/delta/delta.js.map +1 -0
  41. package/dist/commands/delta/print.js +205 -0
  42. package/dist/commands/delta/print.js.map +1 -0
  43. package/dist/commands/dependencies.js +9 -7
  44. package/dist/commands/dependencies.js.map +1 -1
  45. package/dist/commands/diff/diff.js +93 -0
  46. package/dist/commands/diff/diff.js.map +1 -0
  47. package/dist/commands/diff/print.js +38 -0
  48. package/dist/commands/diff/print.js.map +1 -0
  49. package/dist/commands/doctor.js +5 -4
  50. package/dist/commands/doctor.js.map +1 -1
  51. package/dist/commands/explain/explain.js +151 -0
  52. package/dist/commands/explain/explain.js.map +1 -0
  53. package/dist/commands/explore.js +29 -24
  54. package/dist/commands/explore.js.map +1 -1
  55. package/dist/commands/gate/gate.js +115 -0
  56. package/dist/commands/gate/gate.js.map +1 -0
  57. package/dist/commands/gate/print.js +54 -0
  58. package/dist/commands/gate/print.js.map +1 -0
  59. package/dist/commands/gherkin/commit.js +146 -0
  60. package/dist/commands/gherkin/commit.js.map +1 -0
  61. package/dist/commands/gherkin/gherkin.js +246 -0
  62. package/dist/commands/gherkin/gherkin.js.map +1 -0
  63. package/dist/commands/gherkin/reconcile.js +149 -0
  64. package/dist/commands/gherkin/reconcile.js.map +1 -0
  65. package/dist/commands/gherkin/render.js +77 -0
  66. package/dist/commands/gherkin/render.js.map +1 -0
  67. package/dist/commands/init/example.js +204 -0
  68. package/dist/commands/init/example.js.map +1 -0
  69. package/dist/commands/init/first-hour.js +28 -0
  70. package/dist/commands/init/first-hour.js.map +1 -0
  71. package/dist/commands/{init.js → init/init.js} +146 -71
  72. package/dist/commands/init/init.js.map +1 -0
  73. package/dist/commands/init/mcp.js +131 -0
  74. package/dist/commands/init/mcp.js.map +1 -0
  75. package/dist/commands/init/options.js +78 -0
  76. package/dist/commands/init/options.js.map +1 -0
  77. package/dist/commands/instructions.js +72 -14
  78. package/dist/commands/instructions.js.map +1 -1
  79. package/dist/commands/list/campaign/campaign.js +65 -0
  80. package/dist/commands/list/campaign/campaign.js.map +1 -0
  81. package/dist/commands/list/campaign/owners.js +65 -0
  82. package/dist/commands/list/campaign/owners.js.map +1 -0
  83. package/dist/commands/list/json.js +157 -0
  84. package/dist/commands/list/json.js.map +1 -0
  85. package/dist/commands/list/list.js +343 -0
  86. package/dist/commands/list/list.js.map +1 -0
  87. package/dist/commands/list/print.js +301 -0
  88. package/dist/commands/list/print.js.map +1 -0
  89. package/dist/commands/list/review.js +120 -0
  90. package/dist/commands/list/review.js.map +1 -0
  91. package/dist/commands/list/views.js +105 -0
  92. package/dist/commands/list/views.js.map +1 -0
  93. package/dist/commands/mcp/dispatch.js +183 -0
  94. package/dist/commands/mcp/dispatch.js.map +1 -0
  95. package/dist/commands/mcp/mcp.js +34 -0
  96. package/dist/commands/mcp/mcp.js.map +1 -0
  97. package/dist/commands/mcp/serve.js +55 -0
  98. package/dist/commands/mcp/serve.js.map +1 -0
  99. package/dist/commands/migrate-openspec/materialize/active.js +170 -0
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
  101. package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
  102. package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
  103. package/dist/commands/migrate-openspec/materialize/living.js +62 -0
  104. package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
  105. package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
  106. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
  107. package/dist/commands/migrate-openspec/materialize/target.js +219 -0
  108. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
  109. package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
  110. package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
  111. package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
  112. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
  113. package/dist/commands/migrate-openspec/openspec/error.js +19 -0
  114. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
  115. package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
  116. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
  117. package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
  118. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
  119. package/dist/commands/migrate-openspec/openspec/read.js +49 -0
  120. package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
  121. package/dist/commands/migrate-openspec/print.js +89 -0
  122. package/dist/commands/migrate-openspec/print.js.map +1 -0
  123. package/dist/commands/new/commit.js +76 -0
  124. package/dist/commands/new/commit.js.map +1 -0
  125. package/dist/commands/new/new.js +283 -0
  126. package/dist/commands/new/new.js.map +1 -0
  127. package/dist/commands/new/templates.js +355 -0
  128. package/dist/commands/new/templates.js.map +1 -0
  129. package/dist/commands/open.js +203 -0
  130. package/dist/commands/open.js.map +1 -0
  131. package/dist/commands/policy/format.js +152 -0
  132. package/dist/commands/policy/format.js.map +1 -0
  133. package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
  134. package/dist/commands/policy/gate.js.map +1 -0
  135. package/dist/commands/rebase/edit.js +106 -0
  136. package/dist/commands/rebase/edit.js.map +1 -0
  137. package/dist/commands/rebase/living.js +185 -0
  138. package/dist/commands/rebase/living.js.map +1 -0
  139. package/dist/commands/rebase/plan.js +245 -0
  140. package/dist/commands/rebase/plan.js.map +1 -0
  141. package/dist/commands/rebase/rebase.js +275 -0
  142. package/dist/commands/rebase/rebase.js.map +1 -0
  143. package/dist/commands/seed/commit.js +192 -0
  144. package/dist/commands/seed/commit.js.map +1 -0
  145. package/dist/commands/seed/plan.js +133 -0
  146. package/dist/commands/seed/plan.js.map +1 -0
  147. package/dist/commands/seed/seed.js +172 -0
  148. package/dist/commands/seed/seed.js.map +1 -0
  149. package/dist/commands/show/feature.js +178 -0
  150. package/dist/commands/show/feature.js.map +1 -0
  151. package/dist/commands/show/marks.js +24 -0
  152. package/dist/commands/show/marks.js.map +1 -0
  153. package/dist/commands/{show.js → show/service.js} +63 -152
  154. package/dist/commands/show/service.js.map +1 -0
  155. package/dist/commands/show/show.js +77 -0
  156. package/dist/commands/show/show.js.map +1 -0
  157. package/dist/commands/status/json.js +45 -0
  158. package/dist/commands/status/json.js.map +1 -0
  159. package/dist/commands/status/print.js +157 -0
  160. package/dist/commands/status/print.js.map +1 -0
  161. package/dist/commands/status/status.js +119 -0
  162. package/dist/commands/status/status.js.map +1 -0
  163. package/dist/commands/steps/steps.js +189 -0
  164. package/dist/commands/steps/steps.js.map +1 -0
  165. package/dist/commands/subsystem/edit.js +192 -0
  166. package/dist/commands/subsystem/edit.js.map +1 -0
  167. package/dist/commands/subsystem/move.js +265 -0
  168. package/dist/commands/subsystem/move.js.map +1 -0
  169. package/dist/commands/subsystem/query.js +115 -0
  170. package/dist/commands/subsystem/query.js.map +1 -0
  171. package/dist/commands/subsystem/subsystem.js +101 -0
  172. package/dist/commands/subsystem/subsystem.js.map +1 -0
  173. package/dist/commands/subsystem/sync.js +103 -0
  174. package/dist/commands/subsystem/sync.js.map +1 -0
  175. package/dist/commands/subsystem/txn/txn.js +94 -0
  176. package/dist/commands/subsystem/txn/txn.js.map +1 -0
  177. package/dist/commands/subsystem/txn/views.js +70 -0
  178. package/dist/commands/subsystem/txn/views.js.map +1 -0
  179. package/dist/commands/unarchive/manifest.js +160 -0
  180. package/dist/commands/unarchive/manifest.js.map +1 -0
  181. package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
  182. package/dist/commands/unarchive/restore.js.map +1 -0
  183. package/dist/commands/unarchive/unarchive.js +67 -0
  184. package/dist/commands/unarchive/unarchive.js.map +1 -0
  185. package/dist/commands/validate/arch-coverage.js +204 -0
  186. package/dist/commands/validate/arch-coverage.js.map +1 -0
  187. package/dist/commands/validate/checks/fleet-shape.js +296 -0
  188. package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
  189. package/dist/commands/validate/checks/health-deps.js +55 -0
  190. package/dist/commands/validate/checks/health-deps.js.map +1 -0
  191. package/dist/commands/validate/checks/requirements.js +254 -0
  192. package/dist/commands/validate/checks/requirements.js.map +1 -0
  193. package/dist/commands/validate/checks/sources.js +76 -0
  194. package/dist/commands/validate/checks/sources.js.map +1 -0
  195. package/dist/commands/validate/checks/vocabulary.js +31 -0
  196. package/dist/commands/validate/checks/vocabulary.js.map +1 -0
  197. package/dist/commands/validate/feature.js +216 -0
  198. package/dist/commands/validate/feature.js.map +1 -0
  199. package/dist/commands/validate/fleet/census.js +69 -0
  200. package/dist/commands/validate/fleet/census.js.map +1 -0
  201. package/dist/commands/validate/fleet/kind-tags.js +39 -0
  202. package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
  203. package/dist/commands/validate/fleet/landscape.js +353 -0
  204. package/dist/commands/validate/fleet/landscape.js.map +1 -0
  205. package/dist/commands/validate/fleet/load.js +52 -0
  206. package/dist/commands/validate/fleet/load.js.map +1 -0
  207. package/dist/commands/validate/fleet/obligations.js +270 -0
  208. package/dist/commands/validate/fleet/obligations.js.map +1 -0
  209. package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
  210. package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
  211. package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
  212. package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
  213. package/dist/commands/validate/fleet/scorecard/print.js +63 -0
  214. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
  215. package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
  216. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
  217. package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
  218. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
  219. package/dist/commands/validate/fleet/usecases/place.js +13 -0
  220. package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
  221. package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
  222. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
  223. package/dist/commands/validate/fleet/usecases/steps.js +197 -0
  224. package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
  225. package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
  226. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
  227. package/dist/commands/validate/fleet/views/ids.js +40 -0
  228. package/dist/commands/validate/fleet/views/ids.js.map +1 -0
  229. package/dist/commands/validate/fleet/views/stale.js +59 -0
  230. package/dist/commands/validate/fleet/views/stale.js.map +1 -0
  231. package/dist/commands/validate/links/corpus.js +92 -0
  232. package/dist/commands/validate/links/corpus.js.map +1 -0
  233. package/dist/commands/validate/links/glossary.js +75 -0
  234. package/dist/commands/validate/links/glossary.js.map +1 -0
  235. package/dist/commands/validate/report.js +269 -0
  236. package/dist/commands/validate/report.js.map +1 -0
  237. package/dist/commands/validate/service/api.js +338 -0
  238. package/dist/commands/validate/service/api.js.map +1 -0
  239. package/dist/commands/validate/service/events/events.js +219 -0
  240. package/dist/commands/validate/service/events/events.js.map +1 -0
  241. package/dist/commands/validate/service/events/message-producers.js +77 -0
  242. package/dist/commands/validate/service/events/message-producers.js.map +1 -0
  243. package/dist/commands/validate/service/evidence-pins.js +131 -0
  244. package/dist/commands/validate/service/evidence-pins.js.map +1 -0
  245. package/dist/commands/validate/service/service.js +251 -0
  246. package/dist/commands/validate/service/service.js.map +1 -0
  247. package/dist/commands/validate/service/specs.js +251 -0
  248. package/dist/commands/validate/service/specs.js.map +1 -0
  249. package/dist/commands/validate/service/spine.js +113 -0
  250. package/dist/commands/validate/service/spine.js.map +1 -0
  251. package/dist/commands/validate/validate.js +338 -0
  252. package/dist/commands/validate/validate.js.map +1 -0
  253. package/dist/commands/verify/cross/diff.js +182 -0
  254. package/dist/commands/verify/cross/diff.js.map +1 -0
  255. package/dist/commands/verify/evidence/contract.js +32 -0
  256. package/dist/commands/verify/evidence/contract.js.map +1 -0
  257. package/dist/commands/verify/evidence/pins.js +52 -0
  258. package/dist/commands/verify/evidence/pins.js.map +1 -0
  259. package/dist/commands/verify/evidence/read.js +72 -0
  260. package/dist/commands/verify/evidence/read.js.map +1 -0
  261. package/dist/commands/verify/frozen.js +241 -0
  262. package/dist/commands/verify/frozen.js.map +1 -0
  263. package/dist/commands/verify/record.js +226 -0
  264. package/dist/commands/verify/record.js.map +1 -0
  265. package/dist/commands/verify/report.js +223 -0
  266. package/dist/commands/verify/report.js.map +1 -0
  267. package/dist/commands/verify/results.js +246 -0
  268. package/dist/commands/verify/results.js.map +1 -0
  269. package/dist/commands/verify/verify.js +217 -0
  270. package/dist/commands/verify/verify.js.map +1 -0
  271. package/dist/commands/vouch/contract.js +11 -0
  272. package/dist/commands/vouch/contract.js.map +1 -0
  273. package/dist/commands/vouch/pack/contract.js +2 -0
  274. package/dist/commands/vouch/pack/contract.js.map +1 -0
  275. package/dist/commands/vouch/pack/pack.js +266 -0
  276. package/dist/commands/vouch/pack/pack.js.map +1 -0
  277. package/dist/commands/vouch/pack/print.js +271 -0
  278. package/dist/commands/vouch/pack/print.js.map +1 -0
  279. package/dist/commands/vouch/pack/sections.js +142 -0
  280. package/dist/commands/vouch/pack/sections.js.map +1 -0
  281. package/dist/commands/vouch/prompt/confirm.js +46 -0
  282. package/dist/commands/vouch/prompt/confirm.js.map +1 -0
  283. package/dist/commands/vouch/report.js +101 -0
  284. package/dist/commands/vouch/report.js.map +1 -0
  285. package/dist/commands/vouch/run.js +285 -0
  286. package/dist/commands/vouch/run.js.map +1 -0
  287. package/dist/commands/vouch/sample/plan.js +125 -0
  288. package/dist/commands/vouch/sample/plan.js.map +1 -0
  289. package/dist/commands/vouch/sample/print.js +109 -0
  290. package/dist/commands/vouch/sample/print.js.map +1 -0
  291. package/dist/commands/vouch/vet/verify.js +123 -0
  292. package/dist/commands/vouch/vet/verify.js.map +1 -0
  293. package/dist/commands/vouch/vouch.js +176 -0
  294. package/dist/commands/vouch/vouch.js.map +1 -0
  295. package/dist/core/agent/agents-md/artifacts.js +220 -0
  296. package/dist/core/agent/agents-md/artifacts.js.map +1 -0
  297. package/dist/core/agent/agents-md/command-map.js +324 -0
  298. package/dist/core/agent/agents-md/command-map.js.map +1 -0
  299. package/dist/core/agent/agents-md/cycle.js +165 -0
  300. package/dist/core/agent/agents-md/cycle.js.map +1 -0
  301. package/dist/core/agent/agents-md/map/explain.js +34 -0
  302. package/dist/core/agent/agents-md/map/explain.js.map +1 -0
  303. package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
  304. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
  305. package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
  306. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
  307. package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
  308. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
  309. package/dist/core/agent/agents-md/map/mcp.js +41 -0
  310. package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
  311. package/dist/core/agent/agents-md/map/status.js +62 -0
  312. package/dist/core/agent/agents-md/map/status.js.map +1 -0
  313. package/dist/core/agent/agents-md/map/subsystem.js +32 -0
  314. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
  315. package/dist/core/agent/agents-md/map/usecases.js +60 -0
  316. package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
  317. package/dist/core/agent/agents-md/refusals.js +246 -0
  318. package/dist/core/agent/agents-md/refusals.js.map +1 -0
  319. package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
  320. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
  321. package/dist/core/agent/agents-md/spine.js +40 -0
  322. package/dist/core/agent/agents-md/spine.js.map +1 -0
  323. package/dist/core/agent/agents-md.js +170 -0
  324. package/dist/core/agent/agents-md.js.map +1 -0
  325. package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +50 -0
  326. package/dist/core/agent/agents-stamp.js.map +1 -0
  327. package/dist/core/agent/contract.js +2 -0
  328. package/dist/core/agent/contract.js.map +1 -0
  329. package/dist/core/agent/protocol.js +193 -0
  330. package/dist/core/agent/protocol.js.map +1 -0
  331. package/dist/core/agent/scaffold.js +259 -0
  332. package/dist/core/agent/scaffold.js.map +1 -0
  333. package/dist/core/agent/support/report.js +103 -0
  334. package/dist/core/agent/support/report.js.map +1 -0
  335. package/dist/core/agent/tools/dialects.js +150 -0
  336. package/dist/core/agent/tools/dialects.js.map +1 -0
  337. package/dist/core/agent/tools/registry.js +228 -0
  338. package/dist/core/agent/tools/registry.js.map +1 -0
  339. package/dist/core/agent/workflows/adopt.js +133 -0
  340. package/dist/core/agent/workflows/adopt.js.map +1 -0
  341. package/dist/core/agent/workflows/check.js +337 -0
  342. package/dist/core/agent/workflows/check.js.map +1 -0
  343. package/dist/core/agent/workflows/closing.js +262 -0
  344. package/dist/core/agent/workflows/closing.js.map +1 -0
  345. package/dist/core/agent/workflows/feature.js +105 -0
  346. package/dist/core/agent/workflows/feature.js.map +1 -0
  347. package/dist/core/agent/workflows/implement.js +105 -0
  348. package/dist/core/agent/workflows/implement.js.map +1 -0
  349. package/dist/core/agent/workflows/reference/authoring.js +209 -0
  350. package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
  351. package/dist/core/agent/workflows/reference/codes.js +65 -0
  352. package/dist/core/agent/workflows/reference/codes.js.map +1 -0
  353. package/dist/core/agent/workflows/reference/done-check.js +188 -0
  354. package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
  355. package/dist/core/agent/workflows/reference/reference.js +16 -0
  356. package/dist/core/agent/workflows/reference/reference.js.map +1 -0
  357. package/dist/core/agent/workflows/reference/spine.js +297 -0
  358. package/dist/core/agent/workflows/reference/spine.js.map +1 -0
  359. package/dist/core/asyncapi/baseline/plan.js +110 -0
  360. package/dist/core/asyncapi/baseline/plan.js.map +1 -0
  361. package/dist/core/asyncapi/baseline/surfaces.js +54 -0
  362. package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
  363. package/dist/core/asyncapi/depth.js +164 -0
  364. package/dist/core/asyncapi/depth.js.map +1 -0
  365. package/dist/core/asyncapi/digest.js +141 -0
  366. package/dist/core/asyncapi/digest.js.map +1 -0
  367. package/dist/core/asyncapi/merge/components.js +91 -0
  368. package/dist/core/asyncapi/merge/components.js.map +1 -0
  369. package/dist/core/asyncapi/merge/error.js +33 -0
  370. package/dist/core/asyncapi/merge/error.js.map +1 -0
  371. package/dist/core/asyncapi/merge/markers.js +308 -0
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -0
  373. package/dist/core/asyncapi/merge/merge.js +215 -0
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -0
  375. package/dist/core/asyncapi/merge/pin.js +107 -0
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -0
  377. package/dist/core/asyncapi/model.js +11 -0
  378. package/dist/core/asyncapi/model.js.map +1 -0
  379. package/dist/core/asyncapi/producers.js +50 -0
  380. package/dist/core/asyncapi/producers.js.map +1 -0
  381. package/dist/core/{asyncapi.js → asyncapi/read.js} +95 -109
  382. package/dist/core/asyncapi/read.js.map +1 -0
  383. package/dist/core/brief/brief.js +225 -0
  384. package/dist/core/brief/brief.js.map +1 -0
  385. package/dist/core/brief/checks.js +250 -0
  386. package/dist/core/brief/checks.js.map +1 -0
  387. package/dist/core/brief/landscape.js +194 -0
  388. package/dist/core/brief/landscape.js.map +1 -0
  389. package/dist/core/brief/targets.js +226 -0
  390. package/dist/core/brief/targets.js.map +1 -0
  391. package/dist/core/brief/unchecked.js +48 -0
  392. package/dist/core/brief/unchecked.js.map +1 -0
  393. package/dist/core/c4/arch.js +174 -0
  394. package/dist/core/c4/arch.js.map +1 -0
  395. package/dist/core/c4/likec4.js +90 -0
  396. package/dist/core/c4/likec4.js.map +1 -0
  397. package/dist/core/c4/parsed/deployment.js +158 -0
  398. package/dist/core/c4/parsed/deployment.js.map +1 -0
  399. package/dist/core/c4/parsed/dynamic-views.js +132 -0
  400. package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
  401. package/dist/core/c4/parsed/specification.js +78 -0
  402. package/dist/core/c4/parsed/specification.js.map +1 -0
  403. package/dist/core/c4/parsed/values.js +57 -0
  404. package/dist/core/c4/parsed/values.js.map +1 -0
  405. package/dist/core/c4/parsed/view-ids.js +75 -0
  406. package/dist/core/c4/parsed/view-ids.js.map +1 -0
  407. package/dist/core/c4/project/architecture.js +45 -0
  408. package/dist/core/c4/project/architecture.js.map +1 -0
  409. package/dist/core/c4/project/documents.js +58 -0
  410. package/dist/core/c4/project/documents.js.map +1 -0
  411. package/dist/core/c4/project/load.js +187 -0
  412. package/dist/core/c4/project/load.js.map +1 -0
  413. package/dist/core/c4/project/staged.js +220 -0
  414. package/dist/core/c4/project/staged.js.map +1 -0
  415. package/dist/core/c4/resolve/service.js +159 -0
  416. package/dist/core/c4/resolve/service.js.map +1 -0
  417. package/dist/core/c4/resolve/steps.js +144 -0
  418. package/dist/core/c4/resolve/steps.js.map +1 -0
  419. package/dist/core/c4/seed/fleet-file.js +201 -0
  420. package/dist/core/c4/seed/fleet-file.js.map +1 -0
  421. package/dist/core/c4/seed/items.js +91 -0
  422. package/dist/core/c4/seed/items.js.map +1 -0
  423. package/dist/core/c4/seed/stamp.js +48 -0
  424. package/dist/core/c4/seed/stamp.js.map +1 -0
  425. package/dist/core/c4/seed/template.js +109 -0
  426. package/dist/core/c4/seed/template.js.map +1 -0
  427. package/dist/core/c4/source-mask.js +82 -0
  428. package/dist/core/c4/source-mask.js.map +1 -0
  429. package/dist/core/c4/source-scan.js +227 -0
  430. package/dist/core/c4/source-scan.js.map +1 -0
  431. package/dist/core/c4/splice/authored-source.js +101 -0
  432. package/dist/core/c4/splice/authored-source.js.map +1 -0
  433. package/dist/core/c4/splice/contract.js +10 -0
  434. package/dist/core/c4/splice/contract.js.map +1 -0
  435. package/dist/core/c4/splice/delta-blocks.js +139 -0
  436. package/dist/core/c4/splice/delta-blocks.js.map +1 -0
  437. package/dist/core/c4/splice/identity/edges.js +36 -0
  438. package/dist/core/c4/splice/identity/edges.js.map +1 -0
  439. package/dist/core/c4/splice/landscape-merge.js +269 -0
  440. package/dist/core/c4/splice/landscape-merge.js.map +1 -0
  441. package/dist/core/c4/splice/placement.js +194 -0
  442. package/dist/core/c4/splice/placement.js.map +1 -0
  443. package/dist/core/c4/workspace.js +174 -0
  444. package/dist/core/c4/workspace.js.map +1 -0
  445. package/dist/core/capabilities/authoring/notes.js +141 -0
  446. package/dist/core/capabilities/authoring/notes.js.map +1 -0
  447. package/dist/core/capabilities/capabilities.js +124 -0
  448. package/dist/core/capabilities/capabilities.js.map +1 -0
  449. package/dist/core/capabilities/delta/doc.js +50 -0
  450. package/dist/core/capabilities/delta/doc.js.map +1 -0
  451. package/dist/core/capabilities/delta/overlay.js +129 -0
  452. package/dist/core/capabilities/delta/overlay.js.map +1 -0
  453. package/dist/core/capabilities/delta/summary.js +57 -0
  454. package/dist/core/capabilities/delta/summary.js.map +1 -0
  455. package/dist/core/capabilities/delta/tree.js +43 -0
  456. package/dist/core/capabilities/delta/tree.js.map +1 -0
  457. package/dist/core/capabilities/delta/uncovered.js +222 -0
  458. package/dist/core/capabilities/delta/uncovered.js.map +1 -0
  459. package/dist/core/capabilities/findings.js +342 -0
  460. package/dist/core/capabilities/findings.js.map +1 -0
  461. package/dist/core/capabilities/realizes/findings.js +181 -0
  462. package/dist/core/capabilities/realizes/findings.js.map +1 -0
  463. package/dist/core/capabilities/realizes/join.js +143 -0
  464. package/dist/core/capabilities/realizes/join.js.map +1 -0
  465. package/dist/core/capabilities/rollup.js +199 -0
  466. package/dist/core/capabilities/rollup.js.map +1 -0
  467. package/dist/core/capabilities/tree.js +126 -0
  468. package/dist/core/capabilities/tree.js.map +1 -0
  469. package/dist/core/capabilities/usecase-join.js +277 -0
  470. package/dist/core/capabilities/usecase-join.js.map +1 -0
  471. package/dist/core/coherence/authoring/scaffold.js +177 -0
  472. package/dist/core/coherence/authoring/scaffold.js.map +1 -0
  473. package/dist/core/coherence/authoring/sentinels.js +74 -0
  474. package/dist/core/coherence/authoring/sentinels.js.map +1 -0
  475. package/dist/core/coherence/coherence.js +287 -0
  476. package/dist/core/coherence/coherence.js.map +1 -0
  477. package/dist/core/coherence/declared.js +269 -0
  478. package/dist/core/coherence/declared.js.map +1 -0
  479. package/dist/core/coherence/events/declared.js +206 -0
  480. package/dist/core/coherence/events/declared.js.map +1 -0
  481. package/dist/core/coherence/events/events.js +204 -0
  482. package/dist/core/coherence/events/events.js.map +1 -0
  483. package/dist/core/coherence/events/grades.js +166 -0
  484. package/dist/core/coherence/events/grades.js.map +1 -0
  485. package/dist/core/coherence/events/lookups.js +122 -0
  486. package/dist/core/coherence/events/lookups.js.map +1 -0
  487. package/dist/core/coherence/events/merged.js +40 -0
  488. package/dist/core/coherence/events/merged.js.map +1 -0
  489. package/dist/core/coherence/living.js +198 -0
  490. package/dist/core/coherence/living.js.map +1 -0
  491. package/dist/core/coherence/lookups.js +155 -0
  492. package/dist/core/coherence/lookups.js.map +1 -0
  493. package/dist/core/coherence/pending.js +39 -0
  494. package/dist/core/coherence/pending.js.map +1 -0
  495. package/dist/core/conflict-markers.js +60 -0
  496. package/dist/core/conflict-markers.js.map +1 -0
  497. package/dist/core/delta/claims.js +73 -0
  498. package/dist/core/delta/claims.js.map +1 -0
  499. package/dist/core/delta/delta.js +296 -0
  500. package/dist/core/delta/delta.js.map +1 -0
  501. package/dist/core/delta/document.js +177 -0
  502. package/dist/core/delta/document.js.map +1 -0
  503. package/dist/core/delta/scope.js +48 -0
  504. package/dist/core/delta/scope.js.map +1 -0
  505. package/dist/core/delta/select.js +237 -0
  506. package/dist/core/delta/select.js.map +1 -0
  507. package/dist/core/dependencies/dependencies.js +141 -0
  508. package/dist/core/dependencies/dependencies.js.map +1 -0
  509. package/dist/core/dependencies/facts.js +165 -0
  510. package/dist/core/dependencies/facts.js.map +1 -0
  511. package/dist/core/dependencies/fanin.js +88 -0
  512. package/dist/core/dependencies/fanin.js.map +1 -0
  513. package/dist/core/dependencies/graph.js +106 -0
  514. package/dist/core/dependencies/graph.js.map +1 -0
  515. package/dist/core/deployment/delta.js +188 -0
  516. package/dist/core/deployment/delta.js.map +1 -0
  517. package/dist/core/deployment/objects.js +145 -0
  518. package/dist/core/deployment/objects.js.map +1 -0
  519. package/dist/core/diff/base-git.js +148 -0
  520. package/dist/core/diff/base-git.js.map +1 -0
  521. package/dist/core/diff/base-state.js +209 -0
  522. package/dist/core/diff/base-state.js.map +1 -0
  523. package/dist/core/diff/current-state.js +96 -0
  524. package/dist/core/diff/current-state.js.map +1 -0
  525. package/dist/core/diff/scope/changed-paths.js +188 -0
  526. package/dist/core/diff/scope/changed-paths.js.map +1 -0
  527. package/dist/core/diff/semantic.js +243 -0
  528. package/dist/core/diff/semantic.js.map +1 -0
  529. package/dist/core/diff/victims.js +247 -0
  530. package/dist/core/diff/victims.js.map +1 -0
  531. package/dist/core/docs.js +7 -64
  532. package/dist/core/docs.js.map +1 -1
  533. package/dist/core/doctor/agents.js +222 -0
  534. package/dist/core/doctor/agents.js.map +1 -0
  535. package/dist/core/doctor/config.js +135 -0
  536. package/dist/core/doctor/config.js.map +1 -0
  537. package/dist/core/doctor/doctor.js +275 -0
  538. package/dist/core/doctor/doctor.js.map +1 -0
  539. package/dist/core/doctor/report.js +13 -0
  540. package/dist/core/doctor/report.js.map +1 -0
  541. package/dist/core/doctor/residue.js +110 -0
  542. package/dist/core/doctor/residue.js.map +1 -0
  543. package/dist/core/document/apply.js +77 -0
  544. package/dist/core/document/apply.js.map +1 -0
  545. package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
  546. package/dist/core/document/frontmatter.js.map +1 -0
  547. package/dist/core/document/grammar/directives.js +143 -0
  548. package/dist/core/document/grammar/directives.js.map +1 -0
  549. package/dist/core/document/parse.js +263 -0
  550. package/dist/core/document/parse.js.map +1 -0
  551. package/dist/core/document/scenarios.js +221 -0
  552. package/dist/core/document/scenarios.js.map +1 -0
  553. package/dist/core/document/spec.js +241 -0
  554. package/dist/core/document/spec.js.map +1 -0
  555. package/dist/core/{config.js → envelope/config.js} +79 -18
  556. package/dist/core/envelope/config.js.map +1 -0
  557. package/dist/core/envelope/json.js +175 -0
  558. package/dist/core/envelope/json.js.map +1 -0
  559. package/dist/core/{version.js → envelope/version.js} +3 -3
  560. package/dist/core/envelope/version.js.map +1 -0
  561. package/dist/core/explain/families.js +175 -0
  562. package/dist/core/explain/families.js.map +1 -0
  563. package/dist/core/explain/fix-tables.js +178 -0
  564. package/dist/core/explain/fix-tables.js.map +1 -0
  565. package/dist/core/explain/lookup.js +181 -0
  566. package/dist/core/explain/lookup.js.map +1 -0
  567. package/dist/core/explain/refusals.js +49 -0
  568. package/dist/core/explain/refusals.js.map +1 -0
  569. package/dist/core/explain/terms.js +144 -0
  570. package/dist/core/explain/terms.js.map +1 -0
  571. package/dist/core/explore/describe.js +105 -0
  572. package/dist/core/explore/describe.js.map +1 -0
  573. package/dist/core/{explore.js → explore/explore.js} +50 -101
  574. package/dist/core/explore/explore.js.map +1 -0
  575. package/dist/core/fleet-context.js +120 -78
  576. package/dist/core/fleet-context.js.map +1 -1
  577. package/dist/core/gate/checks.js +201 -0
  578. package/dist/core/gate/checks.js.map +1 -0
  579. package/dist/core/gate/partners.js +205 -0
  580. package/dist/core/gate/partners.js.map +1 -0
  581. package/dist/core/gate/report.js +47 -0
  582. package/dist/core/gate/report.js.map +1 -0
  583. package/dist/core/gate/verification.js +113 -0
  584. package/dist/core/gate/verification.js.map +1 -0
  585. package/dist/core/gherkin/digest.js +60 -0
  586. package/dist/core/gherkin/digest.js.map +1 -0
  587. package/dist/core/gherkin/emit.js +177 -0
  588. package/dist/core/gherkin/emit.js.map +1 -0
  589. package/dist/core/gherkin/read.js +76 -0
  590. package/dist/core/gherkin/read.js.map +1 -0
  591. package/dist/core/gherkin/stale.js +234 -0
  592. package/dist/core/gherkin/stale.js.map +1 -0
  593. package/dist/core/gherkin/stamp.js +77 -0
  594. package/dist/core/gherkin/stamp.js.map +1 -0
  595. package/dist/core/gherkin/steps/catalogue.js +154 -0
  596. package/dist/core/gherkin/steps/catalogue.js.map +1 -0
  597. package/dist/core/gherkin/steps/inventory.js +98 -0
  598. package/dist/core/gherkin/steps/inventory.js.map +1 -0
  599. package/dist/core/gherkin/steps/phrase.js +72 -0
  600. package/dist/core/gherkin/steps/phrase.js.map +1 -0
  601. package/dist/core/glossary/delta.js +106 -0
  602. package/dist/core/glossary/delta.js.map +1 -0
  603. package/dist/core/glossary/tree.js +86 -0
  604. package/dist/core/glossary/tree.js.map +1 -0
  605. package/dist/core/kernel/concurrency.js.map +1 -0
  606. package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
  607. package/dist/core/kernel/document-bytes.js.map +1 -0
  608. package/dist/core/kernel/fences.js +42 -0
  609. package/dist/core/kernel/fences.js.map +1 -0
  610. package/dist/core/kernel/ids/capability.js +72 -0
  611. package/dist/core/kernel/ids/capability.js.map +1 -0
  612. package/dist/core/kernel/ids/dirs.js +33 -0
  613. package/dist/core/kernel/ids/dirs.js.map +1 -0
  614. package/dist/core/kernel/ids/feature.js +75 -0
  615. package/dist/core/kernel/ids/feature.js.map +1 -0
  616. package/dist/core/kernel/ids/service.js +156 -0
  617. package/dist/core/kernel/ids/service.js.map +1 -0
  618. package/dist/core/kernel/ids/subsystem.js +63 -0
  619. package/dist/core/kernel/ids/subsystem.js.map +1 -0
  620. package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
  621. package/dist/core/kernel/path-safety.js.map +1 -0
  622. package/dist/core/kernel/records.js.map +1 -0
  623. package/dist/core/links/backlinks.js +68 -0
  624. package/dist/core/links/backlinks.js.map +1 -0
  625. package/dist/core/links/case.js +74 -0
  626. package/dist/core/links/case.js.map +1 -0
  627. package/dist/core/links/corpus.js +104 -0
  628. package/dist/core/links/corpus.js.map +1 -0
  629. package/dist/core/links/findings.js +110 -0
  630. package/dist/core/links/findings.js.map +1 -0
  631. package/dist/core/links/parse.js +236 -0
  632. package/dist/core/links/parse.js.map +1 -0
  633. package/dist/core/mcp/argv.js +111 -0
  634. package/dist/core/mcp/argv.js.map +1 -0
  635. package/dist/core/mcp/author-tools.js +72 -0
  636. package/dist/core/mcp/author-tools.js.map +1 -0
  637. package/dist/core/mcp/framing.js +46 -0
  638. package/dist/core/mcp/framing.js.map +1 -0
  639. package/dist/core/mcp/protocol.js +280 -0
  640. package/dist/core/mcp/protocol.js.map +1 -0
  641. package/dist/core/mcp/tools.js +314 -0
  642. package/dist/core/mcp/tools.js.map +1 -0
  643. package/dist/core/obligations/obligations.js +106 -0
  644. package/dist/core/obligations/obligations.js.map +1 -0
  645. package/dist/core/openapi/baseline/gate.js +166 -0
  646. package/dist/core/openapi/baseline/gate.js.map +1 -0
  647. package/dist/core/openapi/baseline/plan.js +112 -0
  648. package/dist/core/openapi/baseline/plan.js.map +1 -0
  649. package/dist/core/openapi/baseline/record.js +175 -0
  650. package/dist/core/openapi/baseline/record.js.map +1 -0
  651. package/dist/core/openapi/depth.js +122 -0
  652. package/dist/core/openapi/depth.js.map +1 -0
  653. package/dist/core/openapi/digest.js +189 -0
  654. package/dist/core/openapi/digest.js.map +1 -0
  655. package/dist/core/{openapi.js → openapi/doc.js} +44 -106
  656. package/dist/core/openapi/doc.js.map +1 -0
  657. package/dist/core/openapi/merge/components.js +234 -0
  658. package/dist/core/openapi/merge/components.js.map +1 -0
  659. package/dist/core/openapi/merge/error.js +26 -0
  660. package/dist/core/openapi/merge/error.js.map +1 -0
  661. package/dist/core/openapi/merge/markers.js +208 -0
  662. package/dist/core/openapi/merge/markers.js.map +1 -0
  663. package/dist/core/openapi/merge/merge.js +256 -0
  664. package/dist/core/openapi/merge/merge.js.map +1 -0
  665. package/dist/core/openapi/merge/pin.js +128 -0
  666. package/dist/core/openapi/merge/pin.js.map +1 -0
  667. package/dist/core/openspec/decide/capabilities.js +244 -0
  668. package/dist/core/openspec/decide/capabilities.js.map +1 -0
  669. package/dist/core/openspec/decide/dispositions.js +63 -0
  670. package/dist/core/openspec/decide/dispositions.js.map +1 -0
  671. package/dist/core/openspec/decide/renames.js +179 -0
  672. package/dist/core/openspec/decide/renames.js.map +1 -0
  673. package/dist/core/openspec/decide/verdict.js +84 -0
  674. package/dist/core/openspec/decide/verdict.js.map +1 -0
  675. package/dist/core/openspec/inventory.js +221 -0
  676. package/dist/core/openspec/inventory.js.map +1 -0
  677. package/dist/core/openspec/model/mapping.js +76 -0
  678. package/dist/core/openspec/model/mapping.js.map +1 -0
  679. package/dist/core/openspec/model/model.js +65 -0
  680. package/dist/core/openspec/model/model.js.map +1 -0
  681. package/dist/core/openspec/scan/changes.js +235 -0
  682. package/dist/core/openspec/scan/changes.js.map +1 -0
  683. package/dist/core/openspec/scan/config.js +200 -0
  684. package/dist/core/openspec/scan/config.js.map +1 -0
  685. package/dist/core/openspec/scan/shape.js +260 -0
  686. package/dist/core/openspec/scan/shape.js.map +1 -0
  687. package/dist/core/openspec/scan/walk.js +133 -0
  688. package/dist/core/openspec/scan/walk.js.map +1 -0
  689. package/dist/core/openspec/scan/workspace.js +71 -0
  690. package/dist/core/openspec/scan/workspace.js.map +1 -0
  691. package/dist/core/owners/codeowners.js +141 -0
  692. package/dist/core/owners/codeowners.js.map +1 -0
  693. package/dist/core/pack/features.js +120 -0
  694. package/dist/core/pack/features.js.map +1 -0
  695. package/dist/core/pack/joins.js +112 -0
  696. package/dist/core/pack/joins.js.map +1 -0
  697. package/dist/core/pack/living.js +137 -0
  698. package/dist/core/pack/living.js.map +1 -0
  699. package/dist/core/pack/pack.js +76 -0
  700. package/dist/core/pack/pack.js.map +1 -0
  701. package/dist/core/permissions/permissions.js +87 -0
  702. package/dist/core/permissions/permissions.js.map +1 -0
  703. package/dist/core/projection/api.js +84 -0
  704. package/dist/core/projection/api.js.map +1 -0
  705. package/dist/core/projection/arch-slice.js +63 -0
  706. package/dist/core/projection/arch-slice.js.map +1 -0
  707. package/dist/core/projection/events.js +34 -0
  708. package/dist/core/projection/events.js.map +1 -0
  709. package/dist/core/provenance/findings.js +208 -0
  710. package/dist/core/provenance/findings.js.map +1 -0
  711. package/dist/core/provenance/git.js +225 -0
  712. package/dist/core/provenance/git.js.map +1 -0
  713. package/dist/core/provenance/gitq/moves.js +182 -0
  714. package/dist/core/provenance/gitq/moves.js.map +1 -0
  715. package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
  716. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
  717. package/dist/core/provenance/sample/scope.js +238 -0
  718. package/dist/core/provenance/sample/scope.js.map +1 -0
  719. package/dist/core/provenance/sample/sections.js +61 -0
  720. package/dist/core/provenance/sample/sections.js.map +1 -0
  721. package/dist/core/provenance/sources.js +263 -0
  722. package/dist/core/provenance/sources.js.map +1 -0
  723. package/dist/core/provenance/stamp.js +232 -0
  724. package/dist/core/provenance/stamp.js.map +1 -0
  725. package/dist/core/provenance/walk.js +157 -0
  726. package/dist/core/provenance/walk.js.map +1 -0
  727. package/dist/core/repo/authored/paths.js +210 -0
  728. package/dist/core/repo/authored/paths.js.map +1 -0
  729. package/dist/core/repo/entries.js +126 -0
  730. package/dist/core/repo/entries.js.map +1 -0
  731. package/dist/core/repo/paths.js +277 -0
  732. package/dist/core/repo/paths.js.map +1 -0
  733. package/dist/core/repo/repo.js +248 -0
  734. package/dist/core/repo/repo.js.map +1 -0
  735. package/dist/core/repo/service-target.js +153 -0
  736. package/dist/core/repo/service-target.js.map +1 -0
  737. package/dist/core/repo/state.js +58 -0
  738. package/dist/core/repo/state.js.map +1 -0
  739. package/dist/core/repo/tree/find.js +52 -0
  740. package/dist/core/repo/tree/find.js.map +1 -0
  741. package/dist/core/repo/tree/fs.js +64 -0
  742. package/dist/core/repo/tree/fs.js.map +1 -0
  743. package/dist/core/repo/tree/marker.js +61 -0
  744. package/dist/core/repo/tree/marker.js.map +1 -0
  745. package/dist/core/repo/tree/views.js +172 -0
  746. package/dist/core/repo/tree/views.js.map +1 -0
  747. package/dist/core/repo/tree/walk.js +234 -0
  748. package/dist/core/repo/tree/walk.js.map +1 -0
  749. package/dist/core/results.js +99 -2
  750. package/dist/core/results.js.map +1 -1
  751. package/dist/core/scaffold/landscape.js +131 -0
  752. package/dist/core/scaffold/landscape.js.map +1 -0
  753. package/dist/core/scaffold/readme.js +84 -0
  754. package/dist/core/scaffold/readme.js.map +1 -0
  755. package/dist/core/staging/commit.js +241 -0
  756. package/dist/core/staging/commit.js.map +1 -0
  757. package/dist/core/staging/interrupted.js +96 -0
  758. package/dist/core/staging/interrupted.js.map +1 -0
  759. package/dist/core/staging/lock.js +206 -0
  760. package/dist/core/staging/lock.js.map +1 -0
  761. package/dist/core/staging/recovery/finding.js +35 -0
  762. package/dist/core/staging/recovery/finding.js.map +1 -0
  763. package/dist/core/staging/recovery/intent.js +98 -0
  764. package/dist/core/staging/recovery/intent.js.map +1 -0
  765. package/dist/core/staging/recovery/recover.js +126 -0
  766. package/dist/core/staging/recovery/recover.js.map +1 -0
  767. package/dist/core/staging/recovery/residue.js +62 -0
  768. package/dist/core/staging/recovery/residue.js.map +1 -0
  769. package/dist/core/staging/snapshot.js +202 -0
  770. package/dist/core/staging/snapshot.js.map +1 -0
  771. package/dist/core/staging/txn/forward.js +171 -0
  772. package/dist/core/staging/txn/forward.js.map +1 -0
  773. package/dist/core/staging/txn/journal.js +139 -0
  774. package/dist/core/staging/txn/journal.js.map +1 -0
  775. package/dist/core/staging/txn/transaction.js +75 -0
  776. package/dist/core/staging/txn/transaction.js.map +1 -0
  777. package/dist/core/staging/writes.js +113 -0
  778. package/dist/core/staging/writes.js.map +1 -0
  779. package/dist/core/status/actions/execution.js +49 -0
  780. package/dist/core/status/actions/execution.js.map +1 -0
  781. package/dist/core/status/contracts.js +102 -0
  782. package/dist/core/status/contracts.js.map +1 -0
  783. package/dist/core/status/feature/artifacts.js +196 -0
  784. package/dist/core/status/feature/artifacts.js.map +1 -0
  785. package/dist/core/status/feature/feature.js +207 -0
  786. package/dist/core/status/feature/feature.js.map +1 -0
  787. package/dist/core/status/feature/next.js +150 -0
  788. package/dist/core/status/feature/next.js.map +1 -0
  789. package/dist/core/status/feature/state.js +21 -0
  790. package/dist/core/status/feature/state.js.map +1 -0
  791. package/dist/core/status/fleet/fleet.js +197 -0
  792. package/dist/core/status/fleet/fleet.js.map +1 -0
  793. package/dist/core/status/fleet/next.js +207 -0
  794. package/dist/core/status/fleet/next.js.map +1 -0
  795. package/dist/core/status/interrupted.js +97 -0
  796. package/dist/core/status/interrupted.js.map +1 -0
  797. package/dist/core/status/report.js +31 -0
  798. package/dist/core/status/report.js.map +1 -0
  799. package/dist/core/status/scan.js +41 -0
  800. package/dist/core/status/scan.js.map +1 -0
  801. package/dist/core/status/verification.js +201 -0
  802. package/dist/core/status/verification.js.map +1 -0
  803. package/dist/core/usecases/capability.js +169 -0
  804. package/dist/core/usecases/capability.js.map +1 -0
  805. package/dist/core/usecases/delta/claims.js +116 -0
  806. package/dist/core/usecases/delta/claims.js.map +1 -0
  807. package/dist/core/usecases/delta/flows.js +103 -0
  808. package/dist/core/usecases/delta/flows.js.map +1 -0
  809. package/dist/core/usecases/delta/overlay.js +72 -0
  810. package/dist/core/usecases/delta/overlay.js.map +1 -0
  811. package/dist/core/usecases/fleet.js +137 -0
  812. package/dist/core/usecases/fleet.js.map +1 -0
  813. package/dist/core/usecases/operations.js +111 -0
  814. package/dist/core/usecases/operations.js.map +1 -0
  815. package/dist/core/usecases/place.js +44 -0
  816. package/dist/core/usecases/place.js.map +1 -0
  817. package/dist/core/usecases/touch.js +101 -0
  818. package/dist/core/usecases/touch.js.map +1 -0
  819. package/dist/core/verify/answers.js +173 -0
  820. package/dist/core/verify/answers.js.map +1 -0
  821. package/dist/core/verify/build.js +150 -0
  822. package/dist/core/verify/build.js.map +1 -0
  823. package/dist/core/verify/checklist.js +201 -0
  824. package/dist/core/verify/checklist.js.map +1 -0
  825. package/dist/core/verify/claims/identity.js +61 -0
  826. package/dist/core/verify/claims/identity.js.map +1 -0
  827. package/dist/core/verify/cross/diff.js +97 -0
  828. package/dist/core/verify/cross/diff.js.map +1 -0
  829. package/dist/core/verify/evidence/contract.js +169 -0
  830. package/dist/core/verify/evidence/contract.js.map +1 -0
  831. package/dist/core/verify/file.js +257 -0
  832. package/dist/core/verify/file.js.map +1 -0
  833. package/dist/core/verify/pins/lint.js +157 -0
  834. package/dist/core/verify/pins/lint.js.map +1 -0
  835. package/dist/core/verify/pins/pin.js +95 -0
  836. package/dist/core/verify/pins/pin.js.map +1 -0
  837. package/dist/core/verify/record.js +142 -0
  838. package/dist/core/verify/record.js.map +1 -0
  839. package/dist/core/verify/store/commit.js +89 -0
  840. package/dist/core/verify/store/commit.js.map +1 -0
  841. package/dist/core/verify/store/render.js +57 -0
  842. package/dist/core/verify/store/render.js.map +1 -0
  843. package/dist/core/vocabulary/health.js +91 -0
  844. package/dist/core/vocabulary/health.js.map +1 -0
  845. package/dist/core/vocabulary/issue.js +58 -0
  846. package/dist/core/vocabulary/issue.js.map +1 -0
  847. package/dist/core/vocabulary/maturity.js +145 -0
  848. package/dist/core/vocabulary/maturity.js.map +1 -0
  849. package/dist/core/{report.js → vocabulary/report.js} +30 -2
  850. package/dist/core/vocabulary/report.js.map +1 -0
  851. package/dist/core/vocabulary/steps.js +266 -0
  852. package/dist/core/vocabulary/steps.js.map +1 -0
  853. package/dist/core/workspace/discover.js +239 -0
  854. package/dist/core/workspace/discover.js.map +1 -0
  855. package/dist/core/workspace/render.js +52 -0
  856. package/dist/core/workspace/render.js.map +1 -0
  857. package/examples/README.md +189 -0
  858. package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
  859. package/examples/docs/architecture/capabilities.yaml +31 -0
  860. package/examples/docs/architecture/landscape.likec4 +280 -0
  861. package/examples/docs/architecture/obligations.yaml +23 -0
  862. package/examples/docs/architecture/permissions.yaml +50 -0
  863. package/examples/docs/architecture/subsystems.likec4 +11 -0
  864. package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
  865. package/examples/docs/capabilities/checkout/spec.md +55 -0
  866. package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
  867. package/examples/docs/capabilities/order-notifications/spec.md +43 -0
  868. package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
  869. package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
  870. package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
  871. package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
  872. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
  873. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
  874. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
  875. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
  876. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
  877. package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
  878. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
  879. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
  880. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
  881. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
  882. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
  883. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
  884. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
  885. package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
  886. package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
  887. package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
  888. package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
  889. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
  890. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
  891. package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
  892. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
  893. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
  894. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
  895. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
  896. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
  897. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
  898. package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
  899. package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
  900. package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
  901. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
  902. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
  903. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
  904. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
  905. package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
  906. package/examples/docs/glossary/order.md +15 -0
  907. package/examples/docs/glossary/payments/authorization.md +14 -0
  908. package/examples/docs/likec4.config.json +9 -0
  909. package/examples/docs/loam.json +3 -0
  910. package/examples/docs/services/checkout-web/model.likec4 +109 -0
  911. package/examples/docs/services/checkout-web/spec.md +91 -0
  912. package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
  913. package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
  914. package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
  915. package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
  916. package/examples/docs/services/order-service/arch.spec.md +127 -0
  917. package/examples/docs/services/order-service/asyncapi.yaml +108 -0
  918. package/examples/docs/services/order-service/health.yaml +57 -0
  919. package/examples/docs/services/order-service/model.likec4 +138 -0
  920. package/examples/docs/services/order-service/openapi.yaml +257 -0
  921. package/examples/docs/services/order-service/runbook.md +62 -0
  922. package/examples/docs/services/order-service/spec.md +134 -0
  923. package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
  924. package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
  925. package/examples/docs/services/payment-service/arch.spec.md +73 -0
  926. package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
  927. package/examples/docs/services/payment-service/health.yaml +39 -0
  928. package/examples/docs/services/payment-service/model.likec4 +67 -0
  929. package/examples/docs/services/payment-service/openapi.yaml +151 -0
  930. package/examples/docs/services/payment-service/runbook.md +37 -0
  931. package/examples/docs/services/payment-service/spec.md +88 -0
  932. package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
  933. package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
  934. package/examples/docs/services/platform/identity-service/health.yaml +70 -0
  935. package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
  936. package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
  937. package/examples/docs/services/platform/identity-service/runbook.md +95 -0
  938. package/examples/docs/services/platform/identity-service/spec.md +119 -0
  939. package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
  940. package/examples/docs/services/platform/notification-service/health.yaml +35 -0
  941. package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
  942. package/examples/docs/services/platform/notification-service/runbook.md +48 -0
  943. package/examples/docs/services/platform/notification-service/spec.md +112 -0
  944. package/examples/docs/services/platform/subsystem.yaml +2 -0
  945. package/examples/fleet.yaml +57 -0
  946. package/package.json +19 -6
  947. package/dist/commands/adopt.js +0 -240
  948. package/dist/commands/adopt.js.map +0 -1
  949. package/dist/commands/archive.js +0 -1313
  950. package/dist/commands/archive.js.map +0 -1
  951. package/dist/commands/delta.js +0 -388
  952. package/dist/commands/delta.js.map +0 -1
  953. package/dist/commands/docs-repo-gate.js.map +0 -1
  954. package/dist/commands/format.js +0 -17
  955. package/dist/commands/format.js.map +0 -1
  956. package/dist/commands/gherkin.js +0 -309
  957. package/dist/commands/gherkin.js.map +0 -1
  958. package/dist/commands/init.js.map +0 -1
  959. package/dist/commands/list.js +0 -295
  960. package/dist/commands/list.js.map +0 -1
  961. package/dist/commands/migrate-openspec.js +0 -950
  962. package/dist/commands/migrate-openspec.js.map +0 -1
  963. package/dist/commands/new.js +0 -413
  964. package/dist/commands/new.js.map +0 -1
  965. package/dist/commands/rebase.js +0 -356
  966. package/dist/commands/rebase.js.map +0 -1
  967. package/dist/commands/show.js.map +0 -1
  968. package/dist/commands/status.js +0 -262
  969. package/dist/commands/status.js.map +0 -1
  970. package/dist/commands/unarchive.js.map +0 -1
  971. package/dist/commands/validate.js +0 -1773
  972. package/dist/commands/validate.js.map +0 -1
  973. package/dist/commands/verify.js +0 -775
  974. package/dist/commands/verify.js.map +0 -1
  975. package/dist/commands/vouch.js +0 -421
  976. package/dist/commands/vouch.js.map +0 -1
  977. package/dist/core/agent.js +0 -2173
  978. package/dist/core/agent.js.map +0 -1
  979. package/dist/core/agents-stamp.js.map +0 -1
  980. package/dist/core/arch.js +0 -130
  981. package/dist/core/arch.js.map +0 -1
  982. package/dist/core/asyncapi.js.map +0 -1
  983. package/dist/core/brief.js +0 -755
  984. package/dist/core/brief.js.map +0 -1
  985. package/dist/core/coherence.js +0 -577
  986. package/dist/core/coherence.js.map +0 -1
  987. package/dist/core/concurrency.js.map +0 -1
  988. package/dist/core/config.js.map +0 -1
  989. package/dist/core/delta.js +0 -476
  990. package/dist/core/delta.js.map +0 -1
  991. package/dist/core/dependencies.js +0 -356
  992. package/dist/core/dependencies.js.map +0 -1
  993. package/dist/core/doctor.js +0 -596
  994. package/dist/core/doctor.js.map +0 -1
  995. package/dist/core/document-bytes.js.map +0 -1
  996. package/dist/core/explore.js.map +0 -1
  997. package/dist/core/frontmatter.js.map +0 -1
  998. package/dist/core/gherkin.js +0 -463
  999. package/dist/core/gherkin.js.map +0 -1
  1000. package/dist/core/health.js +0 -67
  1001. package/dist/core/health.js.map +0 -1
  1002. package/dist/core/ids.js +0 -122
  1003. package/dist/core/ids.js.map +0 -1
  1004. package/dist/core/issue.js +0 -9
  1005. package/dist/core/issue.js.map +0 -1
  1006. package/dist/core/json.js +0 -74
  1007. package/dist/core/json.js.map +0 -1
  1008. package/dist/core/likec4.js +0 -489
  1009. package/dist/core/likec4.js.map +0 -1
  1010. package/dist/core/maturity.js +0 -66
  1011. package/dist/core/maturity.js.map +0 -1
  1012. package/dist/core/openapi-merge.js +0 -552
  1013. package/dist/core/openapi-merge.js.map +0 -1
  1014. package/dist/core/openapi.js.map +0 -1
  1015. package/dist/core/openspec-inventory.js +0 -1440
  1016. package/dist/core/openspec-inventory.js.map +0 -1
  1017. package/dist/core/path-safety.js.map +0 -1
  1018. package/dist/core/provenance.js +0 -787
  1019. package/dist/core/provenance.js.map +0 -1
  1020. package/dist/core/records.js.map +0 -1
  1021. package/dist/core/repo.js +0 -438
  1022. package/dist/core/repo.js.map +0 -1
  1023. package/dist/core/report.js.map +0 -1
  1024. package/dist/core/spec.js +0 -505
  1025. package/dist/core/spec.js.map +0 -1
  1026. package/dist/core/staging.js +0 -890
  1027. package/dist/core/staging.js.map +0 -1
  1028. package/dist/core/status.js +0 -1132
  1029. package/dist/core/status.js.map +0 -1
  1030. package/dist/core/steps.js +0 -72
  1031. package/dist/core/steps.js.map +0 -1
  1032. package/dist/core/verify.js +0 -807
  1033. package/dist/core/verify.js.map +0 -1
  1034. package/dist/core/version.js.map +0 -1
  1035. /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
  1036. /package/dist/core/{records.js → kernel/records.js} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../../../src/core/c4/resolve/service.ts"],"names":[],"mappings":"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","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/**\n * One `id -> service` resolver per document, not one per lookup.\n *\n * `serviceOf` builds a fresh id map on every call, and this axis calls it twice\n * for every relationship in scope — inside loops that already run over every\n * relationship a service can see. `serviceResolver`'s own doc says it is \"built\n * once per document and shared\"; this is where both axes that need it keep\n * that promise — `../arch.ts`'s `coversEdge` and `./steps.ts`'s `resolvedTier`,\n * each exported and each handed only the element array.\n *\n * A module-level cache is normally the wrong shape here, because a command runs\n * against whatever directory it was invoked in. This one is safe: the key is the\n * per-invocation `Elem[]` a parse produced, its value is a pure function of that\n * key alone, and nothing in it is derived from the working directory — so two\n * runs can never see each other's answer, and the entry dies with the document.\n * Nothing mutates an `Elem[]` after parse.\n *\n * Two tiers because the fleet set is part of the answer: the same document\n * resolves differently with and without `known`, so a cache keyed on the\n * elements alone handed a with-fleet caller the without-fleet resolver.\n * Callers build ONE set per run and pass the same instance through, which is\n * what lets the inner WeakMap key on the set's identity.\n */\nconst RESOLVERS = new WeakMap<Elem[], (id: string) => string>();\nconst FLEET_RESOLVERS = new WeakMap<Elem[], WeakMap<ReadonlySet<string>, (id: string) => string>>();\n\nexport function resolverFor(elements: Elem[], known?: ReadonlySet<string>): (id: string) => string {\n if (known === undefined) {\n const cached = RESOLVERS.get(elements);\n if (cached !== undefined) return cached;\n const resolver = serviceResolver(elements);\n RESOLVERS.set(elements, resolver);\n return resolver;\n }\n let perSet = FLEET_RESOLVERS.get(elements);\n if (perSet === undefined) {\n perSet = new WeakMap();\n FLEET_RESOLVERS.set(elements, perSet);\n }\n const cached = perSet.get(known);\n if (cached !== undefined) return cached;\n const resolver = serviceResolver(elements, known);\n perSet.set(known, resolver);\n return resolver;\n}\n"]}
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Which declared relationship backs one hop of a use case, and what it says the
3
+ * operation is.
4
+ *
5
+ * A `dynamic view`'s hop says "web talks to orders". Answering it is the same
6
+ * shape of join `Covers:` performs for an edge entry one module over — same two
7
+ * tiers, same cached `serviceResolver` (`./service.ts`'s `resolverFor`, which
8
+ * both axes share), same "an endpoint names an element or the service it stands
9
+ * for" rule.
10
+ *
11
+ * It lived in `../arch.ts` while that file was the only home the join had, and
12
+ * moved here when the Covers grammar grew a fourth entry form and the file
13
+ * reached its line limit. The move changed nothing: the seam was already named
14
+ * in that file's own header, and this is the package the resolution it depends
15
+ * on lives in.
16
+ *
17
+ * It does NOT live beside the view reader in `../parsed/dynamic-views.ts`, and
18
+ * that is a rule rather than a preference: a second copy of this join is the
19
+ * copy that drifts, and that module may read `$data` and nothing else
20
+ * (docs/DESIGN.md rule 26) — it has no business holding the model join.
21
+ */
22
+ import {} from "../likec4.js";
23
+ import { resolverFor } from "./service.js";
24
+ /**
25
+ * The pair a step's backing relationship is looked up under. This is the half
26
+ * that mis-grades the whole fleet if it is wrong, because a reply arrow is the
27
+ * commonest step in any sequence diagram.
28
+ *
29
+ * MEASURED at the `likec4@1.59.2` pin, and `test/likec4-view-shape.test.ts` holds
30
+ * the raw form: a reply written `a <- b 'reply'` parses as
31
+ * `{source: "b", target: "a", isBackward: true}` — LikeC4 has ALREADY reversed
32
+ * the endpoints and set the flag — while the forward `b -> a` yields that same
33
+ * pair with no `isBackward` key at all (ABSENT, not false). So the endpoints as
34
+ * parsed describe the direction the MESSAGE travels, and the flag records which
35
+ * arrow the author typed.
36
+ *
37
+ * loam attributes a reply to the CALL it answers, so the reversal is undone
38
+ * here. A landscape declares `web -> orders` because that edge is the
39
+ * dependency; a mirror `orders -> web` for each response is not something a
40
+ * fleet writes down. Reading a reply hop under the message's own direction would
41
+ * report every return hop against such a landscape as `unbacked`, and where a
42
+ * fleet does draw the mirror edge it would file the two halves of one call under
43
+ * two operations — the call under `op 'createOrder'`, its reply under none.
44
+ *
45
+ * That is deliberately NOT what LikeC4's own computed stage does, and the
46
+ * difference is measured rather than assumed: that stage resolves a backward
47
+ * step's `relations` from the UNFLIPPED pair, so at this pin a `web <- orders`
48
+ * step computes to `relations: []` when only `web -> orders` exists, and to the
49
+ * mirror edge when only `orders -> web` does. It is answering which arrow to
50
+ * draw; loam is answering which operation the hop exercises, and reads what a
51
+ * view DECLARES rather than what it shows — which is why loam never reaches that
52
+ * stage at all (docs/DESIGN.md rule 26, enforced by `scripts/arch-check.mjs`).
53
+ * The consequence to know before changing this: where
54
+ * a fleet declares BOTH directions, the mirror edge is not what backs the reply
55
+ * here — the call is, and the mirror edge backs only a hop drawn forward along
56
+ * it.
57
+ */
58
+ function callPair(step) {
59
+ return step.isBackward ? { from: step.target, to: step.source } : { from: step.source, to: step.target };
60
+ }
61
+ /**
62
+ * The distinct `op` values across candidates, first-seen order preserved.
63
+ *
64
+ * Absent is a VALUE here, not a gap to skip: a relationship carrying no `op` and
65
+ * one carrying `createOrder` disagree about the operation just as surely as two
66
+ * different ops do, and skipping the absent one would silently promote that
67
+ * disagreement to a confident answer. `includes` rather than a `Set` because a
68
+ * candidate list is the handful of relationships between one pair of endpoints,
69
+ * and it keeps `undefined` comparing the way the rest of this file expects.
70
+ */
71
+ function distinctOps(rels) {
72
+ const out = [];
73
+ for (const r of rels)
74
+ if (!out.includes(r.op))
75
+ out.push(r.op);
76
+ return out;
77
+ }
78
+ /**
79
+ * The verdict one tier's candidates imply, or `undefined` when that tier found
80
+ * nothing and is therefore not the tier that answers.
81
+ *
82
+ * The count that decides is the DISTINCT-OP count, never the candidate count.
83
+ * That distinction is what makes the service tier safe: once a service is drawn
84
+ * as containers, `a.api -> b` and `a.worker -> b` both carrying
85
+ * `op 'createOrder'` are two relationships and one operation, and a verdict read
86
+ * off `rels.length` would convict a perfectly consistent model of contesting
87
+ * itself.
88
+ */
89
+ function verdictOn(pair, tier, rels) {
90
+ if (rels.length === 0)
91
+ return undefined;
92
+ const ops = distinctOps(rels);
93
+ if (ops.length > 1)
94
+ return { ...pair, verdict: "contested", tier, rels, ops };
95
+ // Length is exactly one here, so the single distinct value is the answer —
96
+ // and `undefined` is a legitimate answer: every candidate agreed that this
97
+ // hop names no operation.
98
+ const [only] = ops;
99
+ return { ...pair, verdict: "attributed", tier, rels, ...(only === undefined ? {} : { op: only }) };
100
+ }
101
+ /**
102
+ * Relationships whose ENDPOINTS resolve to the same two services as the pair —
103
+ * the fallback tier, and the reason a step drawn between two services matches an
104
+ * edge drawn between two containers.
105
+ *
106
+ * Through the module's cached `resolverFor`, not a fresh `serviceResolver`: this
107
+ * runs once per step of every use case in the fleet, and building the id map per
108
+ * step is the cost that comment above `RESOLVERS` exists to prevent.
109
+ */
110
+ function resolvedTier(pair, scope) {
111
+ const resolve = resolverFor(scope.elements, scope.known);
112
+ const from = resolve(pair.from);
113
+ const to = resolve(pair.to);
114
+ return scope.relationships.filter((r) => resolve(r.source) === from && resolve(r.target) === to);
115
+ }
116
+ /**
117
+ * Which relationship in the model backs one hop of a use case, and what it says
118
+ * the operation is.
119
+ *
120
+ * Two tiers, and the second runs ONLY when the first is empty. That ordering is
121
+ * load-bearing rather than an optimisation: the service tier necessarily
122
+ * re-finds every exact match — an endpoint that matches literally resolves to
123
+ * the same service trivially — so running it unconditionally would let a
124
+ * container-level edge with a different `op` contest a step the model already
125
+ * answers exactly, turning an `attributed` verdict into a `contested` one on a
126
+ * document nobody changed.
127
+ *
128
+ * Relationship `kind` is deliberately not part of the match, and that is
129
+ * measured too: at this pin `a -[http]-> b` without a `relationship http` in the
130
+ * `specification` block is "Could not resolve reference to RelationshipKind
131
+ * named 'http'" — a parse error, which under loam's standing rule means no model
132
+ * at all — and `test/helpers/harness.ts`'s own `LANDSCAPE` declares no
133
+ * relationship kinds. So narrowing by kind would mean a fix message asking an
134
+ * author to write a line their document does not parse.
135
+ */
136
+ export function attributeStep(step, scope) {
137
+ const pair = callPair(step);
138
+ const exact = scope.relationships.filter((r) => r.source === pair.from && r.target === pair.to);
139
+ const declared = verdictOn(pair, 1, exact);
140
+ if (declared !== undefined)
141
+ return declared;
142
+ return verdictOn(pair, 2, resolvedTier(pair, scope)) ?? { ...pair, verdict: "unbacked" };
143
+ }
144
+ //# sourceMappingURL=steps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"steps.js","sourceRoot":"","sources":["../../../../src/core/c4/resolve/steps.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Which declared relationship backs one hop of a use case, and what it says the\n * operation is.\n *\n * A `dynamic view`'s hop says \"web talks to orders\". Answering it is the same\n * shape of join `Covers:` performs for an edge entry one module over — same two\n * tiers, same cached `serviceResolver` (`./service.ts`'s `resolverFor`, which\n * both axes share), same \"an endpoint names an element or the service it stands\n * for\" rule.\n *\n * It lived in `../arch.ts` while that file was the only home the join had, and\n * moved here when the Covers grammar grew a fourth entry form and the file\n * reached its line limit. The move changed nothing: the seam was already named\n * in that file's own header, and this is the package the resolution it depends\n * on lives in.\n *\n * It does NOT live beside the view reader in `../parsed/dynamic-views.ts`, and\n * that is a rule rather than a preference: a second copy of this join is the\n * copy that drifts, and that module may read `$data` and nothing else\n * (docs/DESIGN.md rule 26) — it has no business holding the model join.\n */\nimport { type Elem, type Rel } from \"../likec4.js\";\nimport { resolverFor } from \"./service.js\";\nimport type { ParsedStep } from \"../parsed/dynamic-views.js\";\n\n/**\n * The three fields an attribution reads off a step, and no more.\n *\n * Narrower than `ParsedStep` on purpose. Attribution must not depend on the\n * ordinal, the title or the notes — a hop is graded by where it points, never by\n * what the author called it — and a signature that says so is also a signature a\n * test can satisfy with a literal instead of a parsed document. Derived from\n * `ParsedStep` rather than restated, so a rename upstream is a compile error\n * here instead of a second definition that quietly disagrees.\n */\nexport type StepEndpoints = Pick<ParsedStep, \"source\" | \"target\" | \"isBackward\">;\n\n/**\n * What a step is attributed against: the model in view, plus the enumerated\n * fleet where the caller has it.\n *\n * A deliberate subset of `CoverageScope` rather than a reuse of it — an\n * attribution never reads `health`, and demanding one would make every caller\n * fabricate an empty `HealthIds` to ask a question that has nothing to do with\n * alerts. A `CoverageScope` is still structurally assignable here, so a caller\n * already holding the fuller scope passes it through unchanged.\n */\nexport interface StepScope {\n elements: Elem[];\n relationships: Rel[];\n /** As `CoverageScope.known`, for the same reason: one set per run, shared. */\n known?: ReadonlySet<string>;\n}\n\n/** The oriented pair a verdict is about — see `callPair` for which way round. */\ninterface CallPair {\n from: string;\n to: string;\n}\n\n/**\n * What the model says about one hop of a use case.\n *\n * Three variants rather than one record with optional fields, because the fields\n * are not independent: `op` is meaningful only when exactly one operation was\n * implied, `ops` only when more than one was, and an unbacked step has neither\n * and no tier either. Tagged, those states stop being constructible.\n *\n * - `attributed` — the candidates on the matched tier agree, so the hop\n * exercises exactly one operation. `op` is absent when that one agreed\n * operation is *no* operation: the relationships back the hop but none of\n * them carries `metadata { op '…' }`.\n * - `contested` — the candidates disagree, so loam cannot say which operation\n * the hop exercises. `ops` lists the distinct values in candidate order,\n * `undefined` standing for the candidate that declared none.\n * - `unbacked` — nothing in the model backs the hop on either tier. The pair is\n * still carried, because a message about it has to be able to name the edge\n * the author would have to draw.\n *\n * `rels` carries every candidate on the matched tier, not just the first: an\n * `attributed` verdict over two relationships is the normal shape once a service\n * is drawn as containers, and a caller pinning evidence needs all of them.\n */\nexport type StepAttribution =\n | (CallPair & { verdict: \"attributed\"; tier: 1 | 2; rels: readonly Rel[]; op?: string })\n | (CallPair & { verdict: \"contested\"; tier: 1 | 2; rels: readonly Rel[]; ops: readonly (string | undefined)[] })\n | (CallPair & { verdict: \"unbacked\" });\n\n/**\n * The pair a step's backing relationship is looked up under. This is the half\n * that mis-grades the whole fleet if it is wrong, because a reply arrow is the\n * commonest step in any sequence diagram.\n *\n * MEASURED at the `likec4@1.59.2` pin, and `test/likec4-view-shape.test.ts` holds\n * the raw form: a reply written `a <- b 'reply'` parses as\n * `{source: \"b\", target: \"a\", isBackward: true}` — LikeC4 has ALREADY reversed\n * the endpoints and set the flag — while the forward `b -> a` yields that same\n * pair with no `isBackward` key at all (ABSENT, not false). So the endpoints as\n * parsed describe the direction the MESSAGE travels, and the flag records which\n * arrow the author typed.\n *\n * loam attributes a reply to the CALL it answers, so the reversal is undone\n * here. A landscape declares `web -> orders` because that edge is the\n * dependency; a mirror `orders -> web` for each response is not something a\n * fleet writes down. Reading a reply hop under the message's own direction would\n * report every return hop against such a landscape as `unbacked`, and where a\n * fleet does draw the mirror edge it would file the two halves of one call under\n * two operations — the call under `op 'createOrder'`, its reply under none.\n *\n * That is deliberately NOT what LikeC4's own computed stage does, and the\n * difference is measured rather than assumed: that stage resolves a backward\n * step's `relations` from the UNFLIPPED pair, so at this pin a `web <- orders`\n * step computes to `relations: []` when only `web -> orders` exists, and to the\n * mirror edge when only `orders -> web` does. It is answering which arrow to\n * draw; loam is answering which operation the hop exercises, and reads what a\n * view DECLARES rather than what it shows — which is why loam never reaches that\n * stage at all (docs/DESIGN.md rule 26, enforced by `scripts/arch-check.mjs`).\n * The consequence to know before changing this: where\n * a fleet declares BOTH directions, the mirror edge is not what backs the reply\n * here — the call is, and the mirror edge backs only a hop drawn forward along\n * it.\n */\nfunction callPair(step: StepEndpoints): CallPair {\n return step.isBackward ? { from: step.target, to: step.source } : { from: step.source, to: step.target };\n}\n\n/**\n * The distinct `op` values across candidates, first-seen order preserved.\n *\n * Absent is a VALUE here, not a gap to skip: a relationship carrying no `op` and\n * one carrying `createOrder` disagree about the operation just as surely as two\n * different ops do, and skipping the absent one would silently promote that\n * disagreement to a confident answer. `includes` rather than a `Set` because a\n * candidate list is the handful of relationships between one pair of endpoints,\n * and it keeps `undefined` comparing the way the rest of this file expects.\n */\nfunction distinctOps(rels: readonly Rel[]): (string | undefined)[] {\n const out: (string | undefined)[] = [];\n for (const r of rels) if (!out.includes(r.op)) out.push(r.op);\n return out;\n}\n\n/**\n * The verdict one tier's candidates imply, or `undefined` when that tier found\n * nothing and is therefore not the tier that answers.\n *\n * The count that decides is the DISTINCT-OP count, never the candidate count.\n * That distinction is what makes the service tier safe: once a service is drawn\n * as containers, `a.api -> b` and `a.worker -> b` both carrying\n * `op 'createOrder'` are two relationships and one operation, and a verdict read\n * off `rels.length` would convict a perfectly consistent model of contesting\n * itself.\n */\nfunction verdictOn(pair: CallPair, tier: 1 | 2, rels: readonly Rel[]): StepAttribution | undefined {\n if (rels.length === 0) return undefined;\n const ops = distinctOps(rels);\n if (ops.length > 1) return { ...pair, verdict: \"contested\", tier, rels, ops };\n // Length is exactly one here, so the single distinct value is the answer —\n // and `undefined` is a legitimate answer: every candidate agreed that this\n // hop names no operation.\n const [only] = ops;\n return { ...pair, verdict: \"attributed\", tier, rels, ...(only === undefined ? {} : { op: only }) };\n}\n\n/**\n * Relationships whose ENDPOINTS resolve to the same two services as the pair —\n * the fallback tier, and the reason a step drawn between two services matches an\n * edge drawn between two containers.\n *\n * Through the module's cached `resolverFor`, not a fresh `serviceResolver`: this\n * runs once per step of every use case in the fleet, and building the id map per\n * step is the cost that comment above `RESOLVERS` exists to prevent.\n */\nfunction resolvedTier(pair: CallPair, scope: StepScope): Rel[] {\n const resolve = resolverFor(scope.elements, scope.known);\n const from = resolve(pair.from);\n const to = resolve(pair.to);\n return scope.relationships.filter((r) => resolve(r.source) === from && resolve(r.target) === to);\n}\n\n/**\n * Which relationship in the model backs one hop of a use case, and what it says\n * the operation is.\n *\n * Two tiers, and the second runs ONLY when the first is empty. That ordering is\n * load-bearing rather than an optimisation: the service tier necessarily\n * re-finds every exact match — an endpoint that matches literally resolves to\n * the same service trivially — so running it unconditionally would let a\n * container-level edge with a different `op` contest a step the model already\n * answers exactly, turning an `attributed` verdict into a `contested` one on a\n * document nobody changed.\n *\n * Relationship `kind` is deliberately not part of the match, and that is\n * measured too: at this pin `a -[http]-> b` without a `relationship http` in the\n * `specification` block is \"Could not resolve reference to RelationshipKind\n * named 'http'\" — a parse error, which under loam's standing rule means no model\n * at all — and `test/helpers/harness.ts`'s own `LANDSCAPE` declares no\n * relationship kinds. So narrowing by kind would mean a fix message asking an\n * author to write a line their document does not parse.\n */\nexport function attributeStep(step: StepEndpoints, scope: StepScope): StepAttribution {\n const pair = callPair(step);\n const exact = scope.relationships.filter((r) => r.source === pair.from && r.target === pair.to);\n const declared = verdictOn(pair, 1, exact);\n if (declared !== undefined) return declared;\n return verdictOn(pair, 2, resolvedTier(pair, scope)) ?? { ...pair, verdict: \"unbacked\" };\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"]}