@ybotok/loam 0.2.0-alpha.6 → 0.2.0-alpha.9

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 (691) hide show
  1. package/CHANGELOG.md +1477 -1
  2. package/COMPARISON.md +91 -3
  3. package/CONTRIBUTING.md +37 -0
  4. package/MIGRATING-from-OpenSpec.md +40 -22
  5. package/QUICKSTART.md +181 -9
  6. package/README.md +256 -57
  7. package/ROADMAP.md +108 -11
  8. package/SCHEMA.md +721 -130
  9. package/WORKFLOW.md +272 -62
  10. package/dist/cli.js +26 -8
  11. package/dist/cli.js.map +1 -1
  12. package/dist/commands/adopt/adopt.js +5 -10
  13. package/dist/commands/adopt/adopt.js.map +1 -1
  14. package/dist/commands/archive/archive.js +15 -18
  15. package/dist/commands/archive/archive.js.map +1 -1
  16. package/dist/commands/archive/plan/contracts/asyncapi.js +27 -41
  17. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
  18. package/dist/commands/archive/plan/contracts/consumers.js +56 -0
  19. package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
  20. package/dist/commands/archive/plan/contracts/openapi.js +25 -42
  21. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
  22. package/dist/commands/archive/plan/contracts/verdict-lines.js +41 -0
  23. package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
  24. package/dist/commands/archive/plan/gate.js +3 -7
  25. package/dist/commands/archive/plan/gate.js.map +1 -1
  26. package/dist/commands/archive/plan/landscape.js +4 -4
  27. package/dist/commands/archive/plan/landscape.js.map +1 -1
  28. package/dist/commands/archive/plan/refusal.js +8 -18
  29. package/dist/commands/archive/plan/refusal.js.map +1 -1
  30. package/dist/commands/archive/plan/specs.js +134 -25
  31. package/dist/commands/archive/plan/specs.js.map +1 -1
  32. package/dist/commands/archive/plan/state.js +1 -0
  33. package/dist/commands/archive/plan/state.js.map +1 -1
  34. package/dist/commands/archive/run.js +82 -33
  35. package/dist/commands/archive/run.js.map +1 -1
  36. package/dist/commands/archive/shipped.js +49 -0
  37. package/dist/commands/archive/shipped.js.map +1 -0
  38. package/dist/commands/context/context.js +4 -7
  39. package/dist/commands/context/context.js.map +1 -1
  40. package/dist/commands/context/print.js +2 -2
  41. package/dist/commands/context/print.js.map +1 -1
  42. package/dist/commands/delta/delta.js +44 -12
  43. package/dist/commands/delta/delta.js.map +1 -1
  44. package/dist/commands/delta/print.js +1 -1
  45. package/dist/commands/delta/print.js.map +1 -1
  46. package/dist/commands/dependencies.js +3 -7
  47. package/dist/commands/dependencies.js.map +1 -1
  48. package/dist/commands/diff/diff.js +20 -27
  49. package/dist/commands/diff/diff.js.map +1 -1
  50. package/dist/commands/explore.js +4 -8
  51. package/dist/commands/explore.js.map +1 -1
  52. package/dist/commands/gate/gate.js +45 -8
  53. package/dist/commands/gate/gate.js.map +1 -1
  54. package/dist/commands/gate/print.js +15 -3
  55. package/dist/commands/gate/print.js.map +1 -1
  56. package/dist/commands/gherkin/commit.js +13 -54
  57. package/dist/commands/gherkin/commit.js.map +1 -1
  58. package/dist/commands/gherkin/gherkin.js +36 -20
  59. package/dist/commands/gherkin/gherkin.js.map +1 -1
  60. package/dist/commands/gherkin/reconcile.js +10 -13
  61. package/dist/commands/gherkin/reconcile.js.map +1 -1
  62. package/dist/commands/init/first-hour.js +44 -0
  63. package/dist/commands/init/first-hour.js.map +1 -1
  64. package/dist/commands/init/init.js +18 -22
  65. package/dist/commands/init/init.js.map +1 -1
  66. package/dist/commands/list/campaign/campaign.js +4 -6
  67. package/dist/commands/list/campaign/campaign.js.map +1 -1
  68. package/dist/commands/list/json.js +0 -4
  69. package/dist/commands/list/json.js.map +1 -1
  70. package/dist/commands/list/list.js +38 -23
  71. package/dist/commands/list/list.js.map +1 -1
  72. package/dist/commands/list/print.js +38 -6
  73. package/dist/commands/list/print.js.map +1 -1
  74. package/dist/commands/list/suites/suites.js +140 -0
  75. package/dist/commands/list/suites/suites.js.map +1 -0
  76. package/dist/commands/list/views.js +28 -33
  77. package/dist/commands/list/views.js.map +1 -1
  78. package/dist/commands/mcp/dispatch.js +91 -36
  79. package/dist/commands/mcp/dispatch.js.map +1 -1
  80. package/dist/commands/mcp/mcp.js +56 -15
  81. package/dist/commands/mcp/mcp.js.map +1 -1
  82. package/dist/commands/mcp/serve.js.map +1 -1
  83. package/dist/commands/migrate-openspec/materialize/active.js +27 -22
  84. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
  85. package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
  86. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
  87. package/dist/commands/migrate-openspec/materialize/target.js +2 -2
  88. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
  89. package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
  90. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
  91. package/dist/commands/migrate-openspec/openspec/error.js +7 -8
  92. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
  93. package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
  94. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
  95. package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
  96. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
  97. package/dist/commands/new/capability-template.js +118 -0
  98. package/dist/commands/new/capability-template.js.map +1 -0
  99. package/dist/commands/new/commit.js +8 -22
  100. package/dist/commands/new/commit.js.map +1 -1
  101. package/dist/commands/new/modifies.js +206 -0
  102. package/dist/commands/new/modifies.js.map +1 -0
  103. package/dist/commands/new/new.js +59 -14
  104. package/dist/commands/new/new.js.map +1 -1
  105. package/dist/commands/new/templates.js +48 -124
  106. package/dist/commands/new/templates.js.map +1 -1
  107. package/dist/commands/open.js +3 -7
  108. package/dist/commands/open.js.map +1 -1
  109. package/dist/commands/policy/gate.js +27 -0
  110. package/dist/commands/policy/gate.js.map +1 -1
  111. package/dist/commands/rebase/plan.js +2 -2
  112. package/dist/commands/rebase/plan.js.map +1 -1
  113. package/dist/commands/rebase/rebase.js +60 -35
  114. package/dist/commands/rebase/rebase.js.map +1 -1
  115. package/dist/commands/seed/commit.js +55 -26
  116. package/dist/commands/seed/commit.js.map +1 -1
  117. package/dist/commands/seed/plan.js +64 -13
  118. package/dist/commands/seed/plan.js.map +1 -1
  119. package/dist/commands/seed/seed.js +6 -9
  120. package/dist/commands/seed/seed.js.map +1 -1
  121. package/dist/commands/show/show.js +3 -7
  122. package/dist/commands/show/show.js.map +1 -1
  123. package/dist/commands/status/print.js +13 -4
  124. package/dist/commands/status/print.js.map +1 -1
  125. package/dist/commands/status/status.js +3 -7
  126. package/dist/commands/status/status.js.map +1 -1
  127. package/dist/commands/steps/steps.js +3 -7
  128. package/dist/commands/steps/steps.js.map +1 -1
  129. package/dist/commands/subsystem/edit.js +7 -7
  130. package/dist/commands/subsystem/edit.js.map +1 -1
  131. package/dist/commands/subsystem/move.js +60 -25
  132. package/dist/commands/subsystem/move.js.map +1 -1
  133. package/dist/commands/subsystem/query.js +2 -4
  134. package/dist/commands/subsystem/query.js.map +1 -1
  135. package/dist/commands/subsystem/subsystem.js +4 -7
  136. package/dist/commands/subsystem/subsystem.js.map +1 -1
  137. package/dist/commands/subsystem/sync.js +141 -71
  138. package/dist/commands/subsystem/sync.js.map +1 -1
  139. package/dist/commands/subsystem/txn/report.js +55 -5
  140. package/dist/commands/subsystem/txn/report.js.map +1 -1
  141. package/dist/commands/subsystem/txn/txn.js +121 -45
  142. package/dist/commands/subsystem/txn/txn.js.map +1 -1
  143. package/dist/commands/subsystem/txn/views.js +82 -12
  144. package/dist/commands/subsystem/txn/views.js.map +1 -1
  145. package/dist/commands/tasks/render.js +116 -0
  146. package/dist/commands/tasks/render.js.map +1 -0
  147. package/dist/commands/tasks/tasks.js +113 -0
  148. package/dist/commands/tasks/tasks.js.map +1 -0
  149. package/dist/commands/unarchive/manifest.js +6 -9
  150. package/dist/commands/unarchive/manifest.js.map +1 -1
  151. package/dist/commands/unarchive/restore.js +17 -28
  152. package/dist/commands/unarchive/restore.js.map +1 -1
  153. package/dist/commands/unarchive/unarchive.js +22 -30
  154. package/dist/commands/unarchive/unarchive.js.map +1 -1
  155. package/dist/commands/validate/arch-coverage.js +6 -15
  156. package/dist/commands/validate/arch-coverage.js.map +1 -1
  157. package/dist/commands/validate/checks/fleet-shape.js +4 -49
  158. package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
  159. package/dist/commands/validate/checks/health-deps.js +9 -6
  160. package/dist/commands/validate/checks/health-deps.js.map +1 -1
  161. package/dist/commands/validate/checks/requirements.js +18 -8
  162. package/dist/commands/validate/checks/requirements.js.map +1 -1
  163. package/dist/commands/validate/checks/sources.js +6 -2
  164. package/dist/commands/validate/checks/sources.js.map +1 -1
  165. package/dist/commands/validate/feature.js +23 -19
  166. package/dist/commands/validate/feature.js.map +1 -1
  167. package/dist/commands/validate/fleet/census.js +26 -3
  168. package/dist/commands/validate/fleet/census.js.map +1 -1
  169. package/dist/commands/validate/fleet/landscape.js +35 -32
  170. package/dist/commands/validate/fleet/landscape.js.map +1 -1
  171. package/dist/commands/validate/fleet/map/attest.js +7 -12
  172. package/dist/commands/validate/fleet/map/attest.js.map +1 -1
  173. package/dist/commands/validate/fleet/map/bindings.js +16 -5
  174. package/dist/commands/validate/fleet/map/bindings.js.map +1 -1
  175. package/dist/commands/validate/fleet/map/consumers.js +43 -122
  176. package/dist/commands/validate/fleet/map/consumers.js.map +1 -1
  177. package/dist/commands/validate/fleet/map/usecases.js +5 -11
  178. package/dist/commands/validate/fleet/map/usecases.js.map +1 -1
  179. package/dist/commands/validate/fleet/obligations.js +1 -5
  180. package/dist/commands/validate/fleet/obligations.js.map +1 -1
  181. package/dist/commands/validate/fleet/scorecard/print.js +5 -1
  182. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
  183. package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
  184. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
  185. package/dist/commands/validate/fleet/usecases/usecases.js +6 -4
  186. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -1
  187. package/dist/commands/validate/fleet/views/fleet-project.js +1 -1
  188. package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -1
  189. package/dist/commands/validate/fleet/views/ids.js +33 -7
  190. package/dist/commands/validate/fleet/views/ids.js.map +1 -1
  191. package/dist/commands/validate/fleet/views/root-documents.js +17 -35
  192. package/dist/commands/validate/fleet/views/root-documents.js.map +1 -1
  193. package/dist/commands/validate/fleet/views/stale.js +31 -14
  194. package/dist/commands/validate/fleet/views/stale.js.map +1 -1
  195. package/dist/commands/validate/links/corpus.js +1 -4
  196. package/dist/commands/validate/links/corpus.js.map +1 -1
  197. package/dist/commands/validate/links/glossary.js.map +1 -1
  198. package/dist/commands/validate/report.js +5 -4
  199. package/dist/commands/validate/report.js.map +1 -1
  200. package/dist/commands/validate/scope.js +113 -0
  201. package/dist/commands/validate/scope.js.map +1 -0
  202. package/dist/commands/validate/service/api.js +16 -104
  203. package/dist/commands/validate/service/api.js.map +1 -1
  204. package/dist/commands/validate/service/contracts/generated.js +134 -0
  205. package/dist/commands/validate/service/contracts/generated.js.map +1 -0
  206. package/dist/commands/validate/service/events/events.js +1 -1
  207. package/dist/commands/validate/service/events/events.js.map +1 -1
  208. package/dist/commands/validate/service/events/message-producers.js +1 -1
  209. package/dist/commands/validate/service/events/message-producers.js.map +1 -1
  210. package/dist/commands/validate/service/evidence-pins.js +56 -10
  211. package/dist/commands/validate/service/evidence-pins.js.map +1 -1
  212. package/dist/commands/validate/service/service.js +16 -26
  213. package/dist/commands/validate/service/service.js.map +1 -1
  214. package/dist/commands/validate/service/specs.js +31 -38
  215. package/dist/commands/validate/service/specs.js.map +1 -1
  216. package/dist/commands/validate/service/spine.js +8 -11
  217. package/dist/commands/validate/service/spine.js.map +1 -1
  218. package/dist/commands/validate/service/usecases/usecases.js +3 -3
  219. package/dist/commands/validate/service/usecases/usecases.js.map +1 -1
  220. package/dist/commands/validate/validate.js +44 -26
  221. package/dist/commands/validate/validate.js.map +1 -1
  222. package/dist/commands/verify/cross/diff.js +2 -6
  223. package/dist/commands/verify/cross/diff.js.map +1 -1
  224. package/dist/commands/verify/evidence/contract.js +1 -1
  225. package/dist/commands/verify/evidence/contract.js.map +1 -1
  226. package/dist/commands/verify/evidence/read.js +12 -3
  227. package/dist/commands/verify/evidence/read.js.map +1 -1
  228. package/dist/commands/verify/frozen.js +45 -23
  229. package/dist/commands/verify/frozen.js.map +1 -1
  230. package/dist/commands/verify/record.js +69 -17
  231. package/dist/commands/verify/record.js.map +1 -1
  232. package/dist/commands/verify/report.js +27 -44
  233. package/dist/commands/verify/report.js.map +1 -1
  234. package/dist/commands/verify/results.js +112 -30
  235. package/dist/commands/verify/results.js.map +1 -1
  236. package/dist/commands/verify/verify.js +54 -51
  237. package/dist/commands/verify/verify.js.map +1 -1
  238. package/dist/commands/vouch/pack/pack.js +2 -2
  239. package/dist/commands/vouch/pack/pack.js.map +1 -1
  240. package/dist/commands/vouch/pack/print.js +1 -6
  241. package/dist/commands/vouch/pack/print.js.map +1 -1
  242. package/dist/commands/vouch/pack/sections.js +1 -2
  243. package/dist/commands/vouch/pack/sections.js.map +1 -1
  244. package/dist/commands/vouch/run.js +26 -81
  245. package/dist/commands/vouch/run.js.map +1 -1
  246. package/dist/commands/vouch/sample/plan.js +20 -34
  247. package/dist/commands/vouch/sample/plan.js.map +1 -1
  248. package/dist/commands/vouch/vet/verify.js +39 -7
  249. package/dist/commands/vouch/vet/verify.js.map +1 -1
  250. package/dist/commands/vouch/vouch.js +10 -17
  251. package/dist/commands/vouch/vouch.js.map +1 -1
  252. package/dist/core/agent/agents-md/command-map.js +57 -102
  253. package/dist/core/agent/agents-md/command-map.js.map +1 -1
  254. package/dist/core/agent/agents-md/map/explain.js +4 -3
  255. package/dist/core/agent/agents-md/map/explain.js.map +1 -1
  256. package/dist/core/agent/agents-md/map/lenses/context.js +4 -3
  257. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
  258. package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
  259. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
  260. package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
  261. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
  262. package/dist/core/agent/agents-md/map/mcp.js +10 -7
  263. package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
  264. package/dist/core/agent/agents-md/map/status.js +9 -3
  265. package/dist/core/agent/agents-md/map/status.js.map +1 -1
  266. package/dist/core/agent/agents-md/map/subsystem.js +31 -17
  267. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
  268. package/dist/core/agent/agents-md/map/usecases.js +24 -6
  269. package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
  270. package/dist/core/agent/agents-md/refusals.js +37 -21
  271. package/dist/core/agent/agents-md/refusals.js.map +1 -1
  272. package/dist/core/agent/agents-md.js +25 -10
  273. package/dist/core/agent/agents-md.js.map +1 -1
  274. package/dist/core/agent/agents-stamp.js +10 -0
  275. package/dist/core/agent/agents-stamp.js.map +1 -1
  276. package/dist/core/agent/scaffold.js +10 -15
  277. package/dist/core/agent/scaffold.js.map +1 -1
  278. package/dist/core/agent/tools/dialects.js +1 -0
  279. package/dist/core/agent/tools/dialects.js.map +1 -1
  280. package/dist/core/agent/tools/registry.js +41 -76
  281. package/dist/core/agent/tools/registry.js.map +1 -1
  282. package/dist/core/agent/workflows/adopt.js +5 -3
  283. package/dist/core/agent/workflows/adopt.js.map +1 -1
  284. package/dist/core/agent/workflows/check.js +25 -22
  285. package/dist/core/agent/workflows/check.js.map +1 -1
  286. package/dist/core/agent/workflows/closing.js +54 -21
  287. package/dist/core/agent/workflows/closing.js.map +1 -1
  288. package/dist/core/agent/workflows/feature.js +79 -11
  289. package/dist/core/agent/workflows/feature.js.map +1 -1
  290. package/dist/core/agent/workflows/implement.js +91 -13
  291. package/dist/core/agent/workflows/implement.js.map +1 -1
  292. package/dist/core/agent/workflows/reference/authoring.js +3 -1
  293. package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
  294. package/dist/core/agent/workflows/reference/codes.js +8 -6
  295. package/dist/core/agent/workflows/reference/codes.js.map +1 -1
  296. package/dist/core/agent/workflows/reference/done-check.js +37 -15
  297. package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
  298. package/dist/core/agent/workflows/reference/reference.js.map +1 -1
  299. package/dist/core/agent/workflows/reference/spine.js +62 -24
  300. package/dist/core/agent/workflows/reference/spine.js.map +1 -1
  301. package/dist/core/asyncapi/baseline/plan.js +14 -81
  302. package/dist/core/asyncapi/baseline/plan.js.map +1 -1
  303. package/dist/core/asyncapi/digest.js +30 -25
  304. package/dist/core/asyncapi/digest.js.map +1 -1
  305. package/dist/core/asyncapi/merge/components.js +5 -4
  306. package/dist/core/asyncapi/merge/components.js.map +1 -1
  307. package/dist/core/asyncapi/merge/error.js +7 -2
  308. package/dist/core/asyncapi/merge/error.js.map +1 -1
  309. package/dist/core/asyncapi/merge/markers.js +30 -50
  310. package/dist/core/asyncapi/merge/markers.js.map +1 -1
  311. package/dist/core/asyncapi/merge/merge.js +31 -53
  312. package/dist/core/asyncapi/merge/merge.js.map +1 -1
  313. package/dist/core/asyncapi/merge/pin.js +10 -36
  314. package/dist/core/asyncapi/merge/pin.js.map +1 -1
  315. package/dist/core/asyncapi/model.js.map +1 -1
  316. package/dist/core/asyncapi/read.js +36 -12
  317. package/dist/core/asyncapi/read.js.map +1 -1
  318. package/dist/core/brief/brief.js +30 -25
  319. package/dist/core/brief/brief.js.map +1 -1
  320. package/dist/core/brief/landscape.js +8 -12
  321. package/dist/core/brief/landscape.js.map +1 -1
  322. package/dist/core/brief/map/extend.js +8 -5
  323. package/dist/core/brief/map/extend.js.map +1 -1
  324. package/dist/core/brief/map/owed.js +8 -26
  325. package/dist/core/brief/map/owed.js.map +1 -1
  326. package/dist/core/brief/targets.js +79 -5
  327. package/dist/core/brief/targets.js.map +1 -1
  328. package/dist/core/brief/unchecked.js +1 -1
  329. package/dist/core/brief/unchecked.js.map +1 -1
  330. package/dist/core/c4/likec4.js +147 -16
  331. package/dist/core/c4/likec4.js.map +1 -1
  332. package/dist/core/c4/locate/unlocated.js +150 -0
  333. package/dist/core/c4/locate/unlocated.js.map +1 -0
  334. package/dist/core/c4/parsed/values.js +54 -1
  335. package/dist/core/c4/parsed/values.js.map +1 -1
  336. package/dist/core/c4/prefetch/fleet.js +1 -9
  337. package/dist/core/c4/prefetch/fleet.js.map +1 -1
  338. package/dist/core/c4/project/architecture.js +31 -9
  339. package/dist/core/c4/project/architecture.js.map +1 -1
  340. package/dist/core/c4/project/documents.js +36 -10
  341. package/dist/core/c4/project/documents.js.map +1 -1
  342. package/dist/core/c4/project/load.js +14 -21
  343. package/dist/core/c4/project/load.js.map +1 -1
  344. package/dist/core/c4/project/staged.js +7 -3
  345. package/dist/core/c4/project/staged.js.map +1 -1
  346. package/dist/core/c4/resolve/external.js +51 -0
  347. package/dist/core/c4/resolve/external.js.map +1 -0
  348. package/dist/core/c4/resolve/service.js +25 -1
  349. package/dist/core/c4/resolve/service.js.map +1 -1
  350. package/dist/core/c4/seed/template.js +15 -1
  351. package/dist/core/c4/seed/template.js.map +1 -1
  352. package/dist/core/c4/service-model/shape.js +2 -22
  353. package/dist/core/c4/service-model/shape.js.map +1 -1
  354. package/dist/core/c4/source-mask.js +288 -4
  355. package/dist/core/c4/source-mask.js.map +1 -1
  356. package/dist/core/c4/source-scan.js +203 -147
  357. package/dist/core/c4/source-scan.js.map +1 -1
  358. package/dist/core/c4/splice/authored-source.js +19 -0
  359. package/dist/core/c4/splice/authored-source.js.map +1 -1
  360. package/dist/core/c4/splice/delta-blocks.js +5 -18
  361. package/dist/core/c4/splice/delta-blocks.js.map +1 -1
  362. package/dist/core/c4/splice/landscape-merge.js +13 -8
  363. package/dist/core/c4/splice/landscape-merge.js.map +1 -1
  364. package/dist/core/c4/splice/model/merge.js +10 -6
  365. package/dist/core/c4/splice/model/merge.js.map +1 -1
  366. package/dist/core/c4/splice/placement.js +108 -18
  367. package/dist/core/c4/splice/placement.js.map +1 -1
  368. package/dist/core/c4/workspace.js +13 -22
  369. package/dist/core/c4/workspace.js.map +1 -1
  370. package/dist/core/capabilities/capabilities.js +6 -6
  371. package/dist/core/capabilities/capabilities.js.map +1 -1
  372. package/dist/core/capabilities/delta/doc.js +7 -10
  373. package/dist/core/capabilities/delta/doc.js.map +1 -1
  374. package/dist/core/capabilities/findings.js +15 -20
  375. package/dist/core/capabilities/findings.js.map +1 -1
  376. package/dist/core/capabilities/realizes/findings.js +12 -19
  377. package/dist/core/capabilities/realizes/findings.js.map +1 -1
  378. package/dist/core/capabilities/realizes/join.js +0 -20
  379. package/dist/core/capabilities/realizes/join.js.map +1 -1
  380. package/dist/core/capabilities/rollup.js +6 -0
  381. package/dist/core/capabilities/rollup.js.map +1 -1
  382. package/dist/core/capabilities/tree.js +6 -28
  383. package/dist/core/capabilities/tree.js.map +1 -1
  384. package/dist/core/capabilities/usecase-join.js +3 -13
  385. package/dist/core/capabilities/usecase-join.js.map +1 -1
  386. package/dist/core/coherence/authoring/scaffold.js +11 -3
  387. package/dist/core/coherence/authoring/scaffold.js.map +1 -1
  388. package/dist/core/coherence/authoring/sentinels.js +2 -1
  389. package/dist/core/coherence/authoring/sentinels.js.map +1 -1
  390. package/dist/core/coherence/coherence.js +30 -16
  391. package/dist/core/coherence/coherence.js.map +1 -1
  392. package/dist/core/coherence/consumed/operations.js +70 -0
  393. package/dist/core/coherence/consumed/operations.js.map +1 -0
  394. package/dist/core/coherence/consumed/refusal.js +38 -0
  395. package/dist/core/coherence/consumed/refusal.js.map +1 -0
  396. package/dist/core/coherence/declared.js +60 -34
  397. package/dist/core/coherence/declared.js.map +1 -1
  398. package/dist/core/coherence/events/declared.js +44 -59
  399. package/dist/core/coherence/events/declared.js.map +1 -1
  400. package/dist/core/coherence/events/events.js +119 -8
  401. package/dist/core/coherence/events/events.js.map +1 -1
  402. package/dist/core/coherence/events/grades.js +31 -8
  403. package/dist/core/coherence/events/grades.js.map +1 -1
  404. package/dist/core/coherence/events/lookups.js +14 -82
  405. package/dist/core/coherence/events/lookups.js.map +1 -1
  406. package/dist/core/coherence/lookups.js +18 -78
  407. package/dist/core/coherence/lookups.js.map +1 -1
  408. package/dist/core/coherence/retired/archived.js +285 -0
  409. package/dist/core/coherence/retired/archived.js.map +1 -0
  410. package/dist/core/coherence/retired/event-pair.js +62 -0
  411. package/dist/core/coherence/retired/event-pair.js.map +1 -0
  412. package/dist/core/coherence/retired/merged.js +79 -0
  413. package/dist/core/coherence/retired/merged.js.map +1 -0
  414. package/dist/core/coherence/retired/retirement.js +49 -0
  415. package/dist/core/coherence/retired/retirement.js.map +1 -0
  416. package/dist/core/coherence/retired/revival/arrivals.js +131 -0
  417. package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
  418. package/dist/core/coherence/retired/revival/revived.js +297 -0
  419. package/dist/core/coherence/retired/revival/revived.js.map +1 -0
  420. package/dist/core/consumers/after.js +113 -0
  421. package/dist/core/consumers/after.js.map +1 -0
  422. package/dist/core/consumers/consumers.js +296 -0
  423. package/dist/core/consumers/consumers.js.map +1 -0
  424. package/dist/core/consumers/current.js +211 -0
  425. package/dist/core/consumers/current.js.map +1 -0
  426. package/dist/core/consumers/scan.js +46 -0
  427. package/dist/core/consumers/scan.js.map +1 -0
  428. package/dist/core/consumers/state.js +27 -0
  429. package/dist/core/consumers/state.js.map +1 -0
  430. package/dist/core/delta/delta.js +1 -1
  431. package/dist/core/delta/delta.js.map +1 -1
  432. package/dist/core/deployment/delta.js +15 -16
  433. package/dist/core/deployment/delta.js.map +1 -1
  434. package/dist/core/diff/base-git.js +94 -12
  435. package/dist/core/diff/base-git.js.map +1 -1
  436. package/dist/core/diff/base-state.js +28 -16
  437. package/dist/core/diff/base-state.js.map +1 -1
  438. package/dist/core/diff/joins.js +136 -0
  439. package/dist/core/diff/joins.js.map +1 -0
  440. package/dist/core/diff/scope/changed-paths.js +22 -6
  441. package/dist/core/diff/scope/changed-paths.js.map +1 -1
  442. package/dist/core/diff/scope/edges.js +82 -0
  443. package/dist/core/diff/scope/edges.js.map +1 -0
  444. package/dist/core/diff/semantic.js +100 -31
  445. package/dist/core/diff/semantic.js.map +1 -1
  446. package/dist/core/doctor/agents.js +34 -18
  447. package/dist/core/doctor/agents.js.map +1 -1
  448. package/dist/core/doctor/report.js.map +1 -1
  449. package/dist/core/document/apply.js +10 -2
  450. package/dist/core/document/apply.js.map +1 -1
  451. package/dist/core/document/frontmatter.js +55 -0
  452. package/dist/core/document/frontmatter.js.map +1 -1
  453. package/dist/core/document/parse.js +29 -6
  454. package/dist/core/document/parse.js.map +1 -1
  455. package/dist/core/document/scenarios.js +17 -0
  456. package/dist/core/document/scenarios.js.map +1 -1
  457. package/dist/core/document/spec.js +4 -11
  458. package/dist/core/document/spec.js.map +1 -1
  459. package/dist/core/envelope/config.js +10 -0
  460. package/dist/core/envelope/config.js.map +1 -1
  461. package/dist/core/envelope/json.js +34 -0
  462. package/dist/core/envelope/json.js.map +1 -1
  463. package/dist/core/explain/families.js +17 -12
  464. package/dist/core/explain/families.js.map +1 -1
  465. package/dist/core/explain/refusals.js +3 -3
  466. package/dist/core/explain/refusals.js.map +1 -1
  467. package/dist/core/explain/terms.js +18 -9
  468. package/dist/core/explain/terms.js.map +1 -1
  469. package/dist/core/explore/explore.js +2 -7
  470. package/dist/core/explore/explore.js.map +1 -1
  471. package/dist/core/fleet-context.js +34 -95
  472. package/dist/core/fleet-context.js.map +1 -1
  473. package/dist/core/gate/checks.js +10 -3
  474. package/dist/core/gate/checks.js.map +1 -1
  475. package/dist/core/gate/partners.js +64 -19
  476. package/dist/core/gate/partners.js.map +1 -1
  477. package/dist/core/gate/report.js.map +1 -1
  478. package/dist/core/gate/verification.js +75 -12
  479. package/dist/core/gate/verification.js.map +1 -1
  480. package/dist/core/gherkin/stale.js +13 -37
  481. package/dist/core/gherkin/stale.js.map +1 -1
  482. package/dist/core/glossary/tree.js +6 -19
  483. package/dist/core/glossary/tree.js.map +1 -1
  484. package/dist/core/kernel/concurrency.js +19 -0
  485. package/dist/core/kernel/concurrency.js.map +1 -1
  486. package/dist/core/kernel/records.js +11 -0
  487. package/dist/core/kernel/records.js.map +1 -1
  488. package/dist/core/links/backlinks.js +1 -3
  489. package/dist/core/links/backlinks.js.map +1 -1
  490. package/dist/core/links/corpus.js +5 -36
  491. package/dist/core/links/corpus.js.map +1 -1
  492. package/dist/core/mcp/argv.js +101 -0
  493. package/dist/core/mcp/argv.js.map +1 -1
  494. package/dist/core/mcp/author-tools.js +2 -1
  495. package/dist/core/mcp/author-tools.js.map +1 -1
  496. package/dist/core/mcp/protocol.js +2 -2
  497. package/dist/core/mcp/protocol.js.map +1 -1
  498. package/dist/core/mcp/tools.js +43 -33
  499. package/dist/core/mcp/tools.js.map +1 -1
  500. package/dist/core/openapi/baseline/gate.js +53 -12
  501. package/dist/core/openapi/baseline/gate.js.map +1 -1
  502. package/dist/core/openapi/baseline/plan.js +22 -8
  503. package/dist/core/openapi/baseline/plan.js.map +1 -1
  504. package/dist/core/openapi/baseline/record.js +0 -1
  505. package/dist/core/openapi/baseline/record.js.map +1 -1
  506. package/dist/core/openapi/contract-text.js +33 -0
  507. package/dist/core/openapi/contract-text.js.map +1 -0
  508. package/dist/core/openapi/contract-tree.js +56 -0
  509. package/dist/core/openapi/contract-tree.js.map +1 -0
  510. package/dist/core/openapi/depth.js +38 -24
  511. package/dist/core/openapi/depth.js.map +1 -1
  512. package/dist/core/openapi/digest.js +52 -2
  513. package/dist/core/openapi/digest.js.map +1 -1
  514. package/dist/core/openapi/doc.js +12 -4
  515. package/dist/core/openapi/doc.js.map +1 -1
  516. package/dist/core/openapi/merge/components.js +25 -60
  517. package/dist/core/openapi/merge/components.js.map +1 -1
  518. package/dist/core/openapi/merge/error.js +3 -13
  519. package/dist/core/openapi/merge/error.js.map +1 -1
  520. package/dist/core/openapi/merge/markers.js +26 -30
  521. package/dist/core/openapi/merge/markers.js.map +1 -1
  522. package/dist/core/openapi/merge/merge.js +26 -52
  523. package/dist/core/openapi/merge/merge.js.map +1 -1
  524. package/dist/core/openapi/merge/pin.js +15 -35
  525. package/dist/core/openapi/merge/pin.js.map +1 -1
  526. package/dist/core/openspec/decide/verdict.js.map +1 -1
  527. package/dist/core/openspec/inventory.js +4 -5
  528. package/dist/core/openspec/inventory.js.map +1 -1
  529. package/dist/core/openspec/model/mapping.js +30 -28
  530. package/dist/core/openspec/model/mapping.js.map +1 -1
  531. package/dist/core/openspec/model/model.js +12 -7
  532. package/dist/core/openspec/model/model.js.map +1 -1
  533. package/dist/core/openspec/scan/changes.js +10 -4
  534. package/dist/core/openspec/scan/changes.js.map +1 -1
  535. package/dist/core/openspec/scan/config.js +7 -16
  536. package/dist/core/openspec/scan/config.js.map +1 -1
  537. package/dist/core/openspec/scan/shape.js +63 -19
  538. package/dist/core/openspec/scan/shape.js.map +1 -1
  539. package/dist/core/openspec/scan/walk.js +21 -38
  540. package/dist/core/openspec/scan/walk.js.map +1 -1
  541. package/dist/core/pack/living.js +2 -16
  542. package/dist/core/pack/living.js.map +1 -1
  543. package/dist/core/provenance/findings.js +48 -12
  544. package/dist/core/provenance/findings.js.map +1 -1
  545. package/dist/core/provenance/git.js +33 -57
  546. package/dist/core/provenance/git.js.map +1 -1
  547. package/dist/core/provenance/gitq/moves.js +71 -114
  548. package/dist/core/provenance/gitq/moves.js.map +1 -1
  549. package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
  550. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
  551. package/dist/core/provenance/sources.js +23 -6
  552. package/dist/core/provenance/sources.js.map +1 -1
  553. package/dist/core/provenance/stamp.js +5 -17
  554. package/dist/core/provenance/stamp.js.map +1 -1
  555. package/dist/core/repo/entries.js.map +1 -1
  556. package/dist/core/repo/paths.js +34 -29
  557. package/dist/core/repo/paths.js.map +1 -1
  558. package/dist/core/repo/repo.js +24 -14
  559. package/dist/core/repo/repo.js.map +1 -1
  560. package/dist/core/repo/tree/find.js +12 -3
  561. package/dist/core/repo/tree/find.js.map +1 -1
  562. package/dist/core/repo/tree/fs.js +27 -1
  563. package/dist/core/repo/tree/fs.js.map +1 -1
  564. package/dist/core/repo/tree/render/body.js +45 -6
  565. package/dist/core/repo/tree/render/body.js.map +1 -1
  566. package/dist/core/repo/tree/render/service/header.js +125 -0
  567. package/dist/core/repo/tree/render/service/header.js.map +1 -0
  568. package/dist/core/repo/tree/render/service/peers.js +28 -0
  569. package/dist/core/repo/tree/render/service/peers.js.map +1 -0
  570. package/dist/core/repo/tree/render/service/view-id.js +67 -0
  571. package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
  572. package/dist/core/repo/tree/render/service/views.js +78 -0
  573. package/dist/core/repo/tree/render/service/views.js.map +1 -0
  574. package/dist/core/repo/tree/render/stale.js +19 -2
  575. package/dist/core/repo/tree/render/stale.js.map +1 -1
  576. package/dist/core/repo/tree/render/views.js +102 -33
  577. package/dist/core/repo/tree/render/views.js.map +1 -1
  578. package/dist/core/repo/tree/walk.js +8 -30
  579. package/dist/core/repo/tree/walk.js.map +1 -1
  580. package/dist/core/results.js +47 -12
  581. package/dist/core/results.js.map +1 -1
  582. package/dist/core/staging/commit.js +10 -12
  583. package/dist/core/staging/commit.js.map +1 -1
  584. package/dist/core/staging/lock.js +74 -12
  585. package/dist/core/staging/lock.js.map +1 -1
  586. package/dist/core/staging/recovery/intent.js +2 -5
  587. package/dist/core/staging/recovery/intent.js.map +1 -1
  588. package/dist/core/staging/recovery/recover.js +60 -1
  589. package/dist/core/staging/recovery/recover.js.map +1 -1
  590. package/dist/core/staging/snapshot/manifest.js +152 -0
  591. package/dist/core/staging/snapshot/manifest.js.map +1 -0
  592. package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +10 -34
  593. package/dist/core/staging/snapshot/snapshot.js.map +1 -0
  594. package/dist/core/status/actions/execution.js +11 -0
  595. package/dist/core/status/actions/execution.js.map +1 -1
  596. package/dist/core/status/feature/artifacts.js +2 -2
  597. package/dist/core/status/feature/artifacts.js.map +1 -1
  598. package/dist/core/status/feature/feature.js +5 -11
  599. package/dist/core/status/feature/feature.js.map +1 -1
  600. package/dist/core/status/fleet/fleet.js +6 -2
  601. package/dist/core/status/fleet/fleet.js.map +1 -1
  602. package/dist/core/status/fleet/next.js +26 -3
  603. package/dist/core/status/fleet/next.js.map +1 -1
  604. package/dist/core/status/report.js.map +1 -1
  605. package/dist/core/status/verification.js +30 -15
  606. package/dist/core/status/verification.js.map +1 -1
  607. package/dist/core/tasks/packet.js +174 -0
  608. package/dist/core/tasks/packet.js.map +1 -0
  609. package/dist/core/tasks/record.js +43 -0
  610. package/dist/core/tasks/record.js.map +1 -0
  611. package/dist/core/tasks/sources.js +181 -0
  612. package/dist/core/tasks/sources.js.map +1 -0
  613. package/dist/core/tasks/stamps.js +76 -0
  614. package/dist/core/tasks/stamps.js.map +1 -0
  615. package/dist/core/tasks/unit.js +0 -0
  616. package/dist/core/tasks/unit.js.map +1 -0
  617. package/dist/core/usecases/capability.js +11 -14
  618. package/dist/core/usecases/capability.js.map +1 -1
  619. package/dist/core/usecases/delta/flows.js +5 -7
  620. package/dist/core/usecases/delta/flows.js.map +1 -1
  621. package/dist/core/usecases/fleet.js +178 -31
  622. package/dist/core/usecases/fleet.js.map +1 -1
  623. package/dist/core/usecases/operations.js +5 -4
  624. package/dist/core/usecases/operations.js.map +1 -1
  625. package/dist/core/usecases/service/flows.js +9 -6
  626. package/dist/core/usecases/service/flows.js.map +1 -1
  627. package/dist/core/usecases/suites/participants.js +83 -0
  628. package/dist/core/usecases/suites/participants.js.map +1 -0
  629. package/dist/core/usecases/suites/suites.js +45 -0
  630. package/dist/core/usecases/suites/suites.js.map +1 -0
  631. package/dist/core/verify/answers.js +12 -5
  632. package/dist/core/verify/answers.js.map +1 -1
  633. package/dist/core/verify/build.js +102 -82
  634. package/dist/core/verify/build.js.map +1 -1
  635. package/dist/core/verify/checklist.js +45 -10
  636. package/dist/core/verify/checklist.js.map +1 -1
  637. package/dist/core/verify/location.js +67 -0
  638. package/dist/core/verify/location.js.map +1 -0
  639. package/dist/core/verify/pins/lint.js +3 -2
  640. package/dist/core/verify/pins/lint.js.map +1 -1
  641. package/dist/core/verify/pins/pin.js +1 -1
  642. package/dist/core/verify/pins/pin.js.map +1 -1
  643. package/dist/core/verify/read/file.js +224 -0
  644. package/dist/core/verify/read/file.js.map +1 -0
  645. package/dist/core/verify/{file.js → read/shape.js} +16 -104
  646. package/dist/core/verify/read/shape.js.map +1 -0
  647. package/dist/core/verify/read/union.js +105 -0
  648. package/dist/core/verify/read/union.js.map +1 -0
  649. package/dist/core/verify/record.js +35 -12
  650. package/dist/core/verify/record.js.map +1 -1
  651. package/dist/core/verify/store/commit.js +40 -30
  652. package/dist/core/verify/store/commit.js.map +1 -1
  653. package/dist/core/verify/store/render.js +8 -4
  654. package/dist/core/verify/store/render.js.map +1 -1
  655. package/dist/core/vocabulary/issue.js +1 -0
  656. package/dist/core/vocabulary/issue.js.map +1 -1
  657. package/dist/core/vocabulary/maturity.js +53 -3
  658. package/dist/core/vocabulary/maturity.js.map +1 -1
  659. package/examples/README.md +28 -9
  660. package/examples/docs/architecture/landscape.likec4 +21 -7
  661. package/examples/docs/architecture/services.likec4 +70 -0
  662. package/examples/docs/architecture/subsystems.likec4 +3 -0
  663. package/examples/docs/architecture/usecases/order-notification.likec4 +6 -2
  664. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
  665. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
  666. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
  667. package/examples/docs/services/order-service/asyncapi.yaml +3 -2
  668. package/examples/docs/services/order-service/model.likec4 +10 -8
  669. package/examples/docs/services/payment-service/model.likec4 +6 -4
  670. package/examples/docs/services/platform/identity-service/model.likec4 +9 -7
  671. package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
  672. package/examples/docs/services/platform/notification-service/model.likec4 +4 -5
  673. package/package.json +1 -1
  674. package/dist/commands/validate/checks/vocabulary.js +0 -16
  675. package/dist/commands/validate/checks/vocabulary.js.map +0 -1
  676. package/dist/core/agent/agents-md/artifacts.js +0 -221
  677. package/dist/core/agent/agents-md/artifacts.js.map +0 -1
  678. package/dist/core/agent/agents-md/cycle.js +0 -180
  679. package/dist/core/agent/agents-md/cycle.js.map +0 -1
  680. package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
  681. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
  682. package/dist/core/agent/agents-md/spine.js +0 -40
  683. package/dist/core/agent/agents-md/spine.js.map +0 -1
  684. package/dist/core/coherence/events/merged.js +0 -40
  685. package/dist/core/coherence/events/merged.js.map +0 -1
  686. package/dist/core/diff/current-state.js +0 -96
  687. package/dist/core/diff/current-state.js.map +0 -1
  688. package/dist/core/diff/victims.js +0 -256
  689. package/dist/core/diff/victims.js.map +0 -1
  690. package/dist/core/staging/snapshot.js.map +0 -1
  691. package/dist/core/verify/file.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"landscape.js","sourceRoot":"","sources":["../../../src/core/brief/landscape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,aAAa,EAAqB,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,aAAa,EAAqB,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AA0GnE;;;;;GAKG;AACH,KAAK,UAAU,aAAa,CAAC,OAAgB;IAC3C,OAAO,IAAI,GAAG,CAAC,MAAM,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,SAAS,GAAwB,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;AAC5D,MAAM,iBAAiB,GAAsB,CAAC,qBAAqB,CAAC,CAAC;AAErE;;;;;GAKG;AACH,SAAS,aAAa,CAAC,QAA4B;IACjD,MAAM,MAAM,GAAG,CAAC,CAAS,EAAW,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5G,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACtE,CAAC;AAED;;;;;;;;;;;GAWG;AACH,KAAK,UAAU,YAAY,CACzB,OAAgB,EAChB,KAAmB,EACnB,OAAe,EACf,KAA0B;IAE1B,yEAAyE;IACzE,8EAA8E;IAC9E,4EAA4E;IAC5E,4EAA4E;IAC5E,2EAA2E;IAC3E,4EAA4E;IAC5E,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC;QAC/B,aAAa,EAAE,GAAG,EAAE;YAClB,0EAA0E;YAC1E,yEAAyE;YACzE,qDAAqD;QACvD,CAAC;QACD,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC,KAAK;QACxB,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC;KACrC,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACjD,IAAI,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAClE,OAAO,aAAa,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,OAAgB,EAChB,OAAe,EACf,KAAmB;IAEnB,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACpC,+EAA+E;IAC/E,gFAAgF;IAChF,wEAAwE;IACxE,gDAAgD;IAChD,MAAM,WAAW,GAAG,MAAM,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC9D,MAAM,IAAI,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;IAClF,MAAM,KAAK,GAAqB;QAC9B,OAAO,EAAE,UAAU,CAAC,IAAI,CAAC;QACzB,MAAM,EAAE,KAAK;QACb,YAAY,EAAE,KAAK;QACnB,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,EAAE;QACZ,OAAO,EAAE,EAAE;QACX,QAAQ,EAAE,EAAE;QACZ,OAAO,EAAE,EAAE;QACX,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,EAAE;QACZ,WAAW,EAAE,IAAI;KAClB,CAAC;IACF,IAAI,CAAC,KAAK,CAAC,OAAO;QAAE,OAAO,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,cAAc,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IAEpH,yEAAyE;IACzE,4EAA4E;IAC5E,wEAAwE;IACxE,2EAA2E;IAC3E,8EAA8E;IAC9E,2EAA2E;IAC3E,2EAA2E;IAC3E,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC7C,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,0EAA0E;QAC1E,6EAA6E;QAC7E,0DAA0D;QAC1D,kEAAkE;QAClE,qEAAqE;QACrE,8DAA8D;QAC9D,yEAAyE;QACzE,sEAAsE;QACtE,wEAAwE;QACxE,kEAAkE;QAClE,sEAAsE;QACtE,qEAAqE;QACrE,0EAA0E;QAC1E,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QAC5D,OAAO;YACL,GAAG,KAAK;YACR,YAAY,EAAE,OAAO,KAAK,IAAI;YAC9B,OAAO,EAAE,OAAO,IAAI,EAAE;YACtB,WAAW,EAAE,cAAc,CAAC;gBAC1B,GAAG,IAAI;gBACP,OAAO,EAAE,OAAO,IAAI,EAAE;gBACtB,KAAK,EAAE,aAAa;gBACpB,MAAM,EAAE,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC;aACvC,CAAC;SACH,CAAC;IACJ,CAAC;IAED,wEAAwE;IACxE,4EAA4E;IAC5E,8EAA8E;IAC9E,4EAA4E;IAC5E,sEAAsE;IACtE,sEAAsE;IACtE,qEAAqE;IACrE,0EAA0E;IAC1E,2EAA2E;IAC3E,0EAA0E;IAC1E,4EAA4E;IAC5E,4EAA4E;IAC5E,+DAA+D;IAC/D,2CAA2C;IAC3C,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,CAAC,EAAU,EAAW,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;IAC5D,0EAA0E;IAC1E,yEAAyE;IACzE,oCAAoC;IACpC,MAAM,QAAQ,GAAuB,IAAI,CAAC,QAAQ;SAC/C,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;SAChE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACX,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,KAAK,EAAE,CAAC,CAAC,OAAO,KAAK,SAAS;KAC/B,CAAC,CAAC,CAAC;IAEN,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,0EAA0E;IAC1E,4EAA4E;IAC5E,0EAA0E;IAC1E,4EAA4E;IAC5E,sDAAsD;IACtD,MAAM,IAAI,GAAG,CAAC,CAAqC,EAAW,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAClG,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACnC,IAAI,IAAI,CAAC,CAAC,CAAC;YAAE,SAAS;QACtB,MAAM,IAAI,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;QAC1D,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;aAChE,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAgB,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC;IACjG,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACnG,MAAM,QAAQ,GACZ,OAAO,KAAK,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,YAAY,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACzG,MAAM,GAAG,GAAG;QACV,OAAO;QACP,OAAO;QACP,WAAW;QACX,QAAQ;QACR,WAAW,EAAE,aAAa,CAAC,QAAQ,CAAC;QACpC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE;QAC1B,6EAA6E;QAC7E,wEAAwE;QACxE,8CAA8C;QAC9C,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,KAAK;KAC5D,CAAC;IACF,MAAM,WAAW,GAAG,CAAC,QAAQ;QAC3B,CAAC,CAAC,cAAc,CAAC,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;QACjD,CAAC,CAAC,OAAO,KAAK,KAAK;YACjB,CAAC,CAAC,cAAc,CAAC,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;YAC/C,CAAC,CAAC,IAAI,CAAC;IACX,OAAO;QACL,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,IAAI;QACZ,YAAY,EAAE,IAAI;QAClB,QAAQ;QACR,QAAQ;QACR,OAAO;QACP,QAAQ;QACR,OAAO;QACP,OAAO;QACP,QAAQ;QACR,WAAW;KACZ,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,KAAK,UAAU,eAAe,CAAC,IAAY,EAAE,OAAe,EAAE,KAA0B;IACtF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QACvC,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACnD,MAAM,GAAG,GAAG,GAAG,CAAC,aAAa;aAC1B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC;aAC1C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAChB,MAAM,CAAC,CAAC,EAAE,EAAgB,EAAE,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC;QAClD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC","sourcesContent":["/**\n * The fleet-map half of the adoption brief, READ side: what the fleet map\n * already says about one service — its elements, its inbound and outbound\n * edges, the operations the fleet already calls on it — and which of the four\n * states the map is in for it. The write the service still owes the file — the\n * block, when nothing in it resolves to the service yet, or the EDGES, when an\n * element does and no edge touches it — is spelled in `./map/owed.ts`\n * (`landscapeArtifact`, `instructionFor`), which this module hands its findings\n * to. Both halves exist to stop an agent inventing a parallel model: the fleet\n * has already drawn some of these boxes and edges, and a baseline has to attach.\n *\n * \"The fleet map\" is the `architecture/` PROJECT — the landscape plus every\n * other `.likec4` under it, minus the generated views file — and not the\n * landscape FILE. The two are the same document set `validate --all` grades\n * `landscape.service-isolated` over, and reading one file here let the two\n * surfaces disagree about the same tree.\n */\nimport { existsSync } from \"node:fs\";\nimport { loadFile } from \"../c4/likec4.js\";\nimport { loadArchitecture } from \"../c4/project/architecture.js\";\nimport { attestedCalls, type AttestedCall } from \"../c4/resolve/attested.js\";\nimport { serviceResolver } from \"../c4/resolve/service.js\";\nimport { ServiceModels } from \"../c4/prefetch/fleet.js\";\nimport { OBLIGATION_TAG_PREFIX } from \"../obligations/obligations.js\";\nimport { landscapePath, type ServicePaths } from \"../repo/paths.js\";\nimport { enumeratedServiceIds, serviceTreePathOf } from \"../repo/service-target.js\";\nimport { EXTERNAL_TAG, GRADED_TAGS } from \"../vocabulary/maturity.js\";\nimport { brokenDocuments, instructionFor } from \"./map/owed.js\";\nimport { properAncestorIds } from \"../kernel/ids/fqn/ancestors.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\n\n/* ------------------------------------------------------------------ */\n/* What the fleet already says about this service */\n/* ------------------------------------------------------------------ */\n\nexport interface LandscapeElement {\n id: string;\n title: string;\n kind: string;\n tags: string[];\n /** True when the element binds explicitly with `metadata { service }`. */\n bound: boolean;\n}\n\nexport interface LandscapeEdge {\n from?: string;\n to?: string;\n op: string | null;\n title: string | null;\n}\n\nexport interface LandscapeContext {\n /** Whether `architecture/landscape.likec4` — the file the brief asks for edits to — is on disk. */\n present: boolean;\n /**\n * False when the landscape exists but the `architecture/` project does not\n * parse. A sibling under `architecture/` counts: a broken use case leaves the\n * map unusable for `validate --all` too, and a brief that read the landscape\n * alone would report a fleet nobody can render.\n */\n parses: boolean;\n /**\n * Whether loam could read WHO CALLS this service — the only evidence an EMPTY\n * `expects` may be read as \"nothing calls it\".\n *\n * A separate fact from `parses` because they answer about different documents.\n * `parses` is about the whole `architecture/` PROJECT (W4), and the callers\n * live in the landscape FILE: a broken palette or use case beside a landscape\n * that reads perfectly left `parses` false, and `apiExpected` then demanded an\n * OpenAPI contract of a browser UI the map proves nobody calls — while `loam\n * list` and `loam context`, which read the file (core/pack/living.ts), said the\n * opposite about the same tree.\n */\n callersKnown: boolean;\n /** Whether any element resolves to this service — null when nothing could be read. */\n modelled: boolean | null;\n /**\n * The service-LEVEL elements that resolve to this service: an element whose\n * ancestor also resolves to it is one of its containers, not a second box.\n */\n elements: LandscapeElement[];\n /**\n * The map's edges into and out of this service, INTERNAL edges excluded: an\n * edge whose two endpoints both resolve here (`svc.web -> svc.db`) is this\n * service's own wiring, and filed as inbound it named the service as its own\n * caller.\n */\n inbound: LandscapeEdge[];\n outbound: LandscapeEdge[];\n /** Operations other services already call on this one: the contract owes them. */\n expects: string[];\n /**\n * Whether at least one relationship in the map has an endpoint resolving to\n * this service — read off the relationships, NOT off `inbound`/`outbound`,\n * because an intra-service edge counts here (it is `landscape.service-\n * isolated`'s predicate) while those two lists drop it. `null` exactly when\n * `modelled` is `null` or `false`. It exists\n * because `instruction` used to go null on element existence alone, and\n * `loam seed` writes a bound, edgeless element for every service nobody\n * listed under `calls:` — so the brief declared the map finished for exactly\n * the services the map had not reached.\n *\n * Binary by design, and the fifth silent case of `landscape.service-isolated`\n * with it: ONE edge closes the state, so a map drawing one of five attested\n * calls reports `touched: true`, `attested: []` and no instruction. The check\n * is touched/untouched, not a set difference — loam cannot tell a call the map\n * omits from a call it deliberately does not draw at fleet level, and a\n * difference reported as an omission would be an invented edge with a code\n * beside it.\n */\n touched: boolean | null;\n /**\n * The calls `services/<…>/<svc>/model.likec4` declares ACROSS its boundary\n * (`attestedCalls`, core/c4/resolve/attested.ts). `[]` when the model is\n * absent, unparseable, or declares none — deliberately not told apart, since\n * the only thing the brief may do with the list is name edges ALREADY\n * ATTESTED, never invent one. Read only when `touched` is `false`: every\n * other state either has edges (nothing owed) or no element (the block, not\n * the edges, is owed), and a first adoption has no model to read anyway.\n */\n attested: AttestedCall[];\n /**\n * The write this service still owes the fleet map, in prose — null once an\n * element resolves to it AND an edge touches that element. It duplicates the\n * `landscape.likec4` target's shape rules on purpose: an agent driving off\n * `--json` reads `landscape` to learn what the fleet already says, and every\n * `targets[]` consumer walks the artifact list. Only one of those two readers\n * used to exist, so `adopt` briefed eight files and never once said the ninth\n * thing — that a documented service nobody drew is invisible to the whole\n * cross-service layer.\n */\n instruction: string | null;\n}\n\n/**\n * The service ids the resolver may fall back on when an element carries no\n * `metadata { service }` binding — the same positive evidence `validate` and\n * `list` hand it. A docs repo with no `services/` yet (`adopt` runs there)\n * leaves the resolver with bindings alone, which is what it had before.\n */\nasync function knownServices(docsDir: DocsDir): Promise<ReadonlySet<string>> {\n return new Set(await enumeratedServiceIds(docsDir));\n}\n\n/**\n * The tags loam reads on an ELEMENT. `GRADED_TAGS` is the vocabulary's own\n * list of the tags that change what loam concludes — spelled once there, so\n * the day a third tag is graded this set follows and the instruction below\n * does not tell an agent to clear it as unread. The obligation prefix is the\n * one tag PREFIX loam reads on an element; `#cap-`/`#req-` are view tags and\n * never sit on one, so they are not in this list.\n */\nconst LOAM_TAGS: ReadonlySet<string> = new Set(GRADED_TAGS);\nconst LOAM_TAG_PREFIXES: readonly string[] = [OBLIGATION_TAG_PREFIX];\n\n/**\n * The element's tags loam does NOT read — named in the instruction and never\n * interpreted. A placeholder convention (\"drawn ahead of adoption\") is the\n * fleet's own, `loam seed` writes none, and a view written `exclude\n * element.tag = #<that>` keeps an adopted service hidden until it is cleared.\n */\nfunction foreignTagsOf(elements: LandscapeElement[]): string[] {\n const unread = (t: string): boolean => !LOAM_TAGS.has(t) && !LOAM_TAG_PREFIXES.some((p) => t.startsWith(p));\n return [...new Set(elements.flatMap((e) => e.tags))].filter(unread);\n}\n\n/**\n * The attested calls, or nothing off a model that could not be read: `c4.invalid`\n * is validate's finding, and an edge list read off half a document is invention.\n *\n * Through the ONE reader of `model.likec4` (`core/c4/service-model/load.ts`),\n * because the two shapes are read differently and this brief is handed to an\n * agent that will act on it: a model that EXTENDS the fleet map, opened as a\n * lone file, comes back as a pile of parse errors — so the brief would tell\n * every migrated service that its model attests nothing and quietly drop the\n * edges the map most likely owes. `mapUnreadable` is the same empty answer for\n * the same reason: an extending model whose map does not parse was never read.\n */\nasync function attestedFrom(\n docsDir: DocsDir,\n paths: ServicePaths,\n service: string,\n known: ReadonlySet<string>,\n): Promise<AttestedCall[]> {\n // The memo directly rather than through `core/fleet-context.ts`, and the\n // reason is the package graph: this module already depends on `core/c4/`, and\n // reaching for the read index would add a `brief -> core-root` edge for one\n // read in a command that grades a single service. The three things the memo\n // cannot know for itself are supplied here — the enumeration this function\n // was already handed, the per-file loader, and a counter nobody is keeping.\n const models = new ServiceModels({\n onProjectLoad: () => {\n // `adopt` reports no read counters, so there is nothing to increment. The\n // hook is the memo's way of letting a caller own its own statistics, and\n // an empty one is that answer, not a forgotten line.\n },\n known: async () => known,\n standalone: (path) => loadFile(path),\n });\n const model = await models.model(docsDir, paths);\n if (model.mapUnreadable || model.doc.errors.length > 0) return [];\n return attestedCalls(model.doc, service, known);\n}\n\n/**\n * What the living fleet map already says about this service — the half of the\n * brief that stops an agent inventing a parallel model: the fleet has already\n * drawn some of these boxes and edges, and the baseline has to attach to them.\n * An `architecture/` project that does not parse yields `modelled: null` —\n * \"nothing models it\" would be a claim about documents nobody could read; the\n * landscape FILE parsing on its own does not rescue it, because the map the\n * renderer and `validate --all` see is the whole project. `paths` is the\n * service's own artifact set, located by the caller through the enumeration\n * (never joined at `services/<id>/`): the model is opened in exactly one state —\n * an element with no edge — to name the calls the map most likely owes. The\n * WHOLE record travels rather than the model path alone, because the model is\n * now read through its own shape and an extending one is read as a project of\n * the service's directory plus the map. It is required rather than optional\n * because the edgeless instruction states what that file attests, and a caller\n * that omitted it would get a brief asserting a fact about a document loam never\n * opened.\n */\nexport async function landscapeContext(\n docsDir: DocsDir,\n service: string,\n paths: ServicePaths,\n): Promise<LandscapeContext> {\n const path = landscapePath(docsDir);\n // The service's own directory, spelled from the enumeration rather than joined\n // at the root: this text is handed to an AGENT that will go and edit files, and\n // a filed service named at `services/<id>/` sends it to create a second\n // directory beside the one that already exists.\n const servicePath = await serviceTreePathOf(docsDir, service);\n const bare = { service, expects: [], servicePath, attested: [], foreignTags: [] };\n const empty: LandscapeContext = {\n present: existsSync(path),\n parses: false,\n callersKnown: false,\n modelled: null,\n elements: [],\n inbound: [],\n outbound: [],\n expects: [],\n touched: null,\n attested: [],\n instruction: null,\n };\n if (!empty.present) return { ...empty, modelled: false, instruction: instructionFor({ ...bare, state: \"absent\" }) };\n\n // The `architecture/` PROJECT, through the one loader every other reader\n // shares — never `loadFile(path)`. A fleet spreads its model over more than\n // the landscape (a second `model { }` block in a sibling, a use case, a\n // palette), and `landscape.service-isolated` grades `touched` over exactly\n // this set: reading the landscape alone made `adopt` report `touched: false`,\n // the attested calls and an edit target for a service the fleet run on the\n // same tree called matched, with nothing to tell an agent which was right.\n const known = await knownServices(docsDir);\n const land = await loadArchitecture(docsDir);\n if (land.errors.length > 0) {\n // Since W4 this arm answers for the whole `architecture/` PROJECT, so the\n // document that broke is very often a SIBLING beside a landscape that parses\n // perfectly — and two things followed from not saying so:\n // - the instruction told an agent to go and fix parse errors in\n // `architecture/landscape.likec4`, which has none, contradicting\n // `validate --all` run one second later on the same tree;\n // - the API question — who CALLS this service — was answered \"unknown\"\n // off a landscape file loam can read, so a broken palette flipped\n // `openapi.yaml` to required for a browser UI the map proves nobody\n // calls, while `list` and `context` (which read the FILE, see\n // core/pack/living.ts) answered the opposite about the same tree.\n // Nothing else is rescued: `modelled`, `elements` and `touched` stay\n // unknown, because those are claims about the project the renderer loads.\n const expects = await expectsFromFile(path, service, known);\n return {\n ...empty,\n callersKnown: expects !== null,\n expects: expects ?? [],\n instruction: instructionFor({\n ...bare,\n expects: expects ?? [],\n state: \"unparseable\",\n broken: brokenDocuments(docsDir, land),\n }),\n };\n }\n\n // One resolver for the whole scan, built with the real service ids, and\n // built BEFORE the element filter: \"an element resolves to this service\" is\n // the resolver's answer — a nearest-ancestor binding wins over a descendant's\n // title — and it is the answer `validate --all` grades `service-unmodelled`\n // on and `loam context` prints `modelled` from. This used to ask each\n // element for its OWN binding-or-title instead, so a container titled\n // 'payment-service' inside an element bound to order-service read as\n // payment-service's box, and the edgeless arm told an agent \"do not add a\n // second element\" for a service the fleet run reported unmodelled (error).\n // The same resolver serves the edges: one drawn into a modelled container\n // (`paymentService.api`) is an edge into the service, and `expects` — which\n // decides whether this service owes an OpenAPI contract at all — has to see\n // it. Building it once also stops the per-edge rebuild the old\n // `serviceOf(land.elements, id)` call did.\n const svcOf = serviceResolver(land.elements, known);\n const mine = (id: string): boolean => svcOf(id) === service;\n // The service-LEVEL elements only: one that resolves to the service while\n // an ancestor does too is a container of it, and listing it would show a\n // second box the map does not have.\n const elements: LandscapeElement[] = land.elements\n .filter((e) => mine(e.id) && !properAncestorIds(e.id).some(mine))\n .map((e) => ({\n id: e.id,\n title: e.title,\n kind: e.kind,\n tags: e.tags,\n bound: e.service !== undefined,\n }));\n\n const inbound: LandscapeEdge[] = [];\n const outbound: LandscapeEdge[] = [];\n // An edge whose BOTH endpoints resolve here is the service's own internal\n // wiring: filed as inbound it made the service its own caller (verification\n // 2026-09-04). It still TOUCHES the service, so `touched` below reads the\n // relationships rather than these lists — the isolation check counts either\n // endpoint, and the two predicates must not disagree.\n const self = (r: { source: string; target: string }): boolean => mine(r.source) && mine(r.target);\n for (const r of land.relationships) {\n if (self(r)) continue;\n const edge = { op: r.op ?? null, title: r.title ?? null };\n if (mine(r.target)) inbound.push({ from: svcOf(r.source), ...edge });\n else if (mine(r.source)) outbound.push({ to: svcOf(r.target), ...edge });\n }\n\n const expects = [...new Set(inbound.map((e) => e.op).filter((op): op is string => op !== null))];\n const modelled = land.elements.some((e) => mine(e.id));\n const touched = modelled ? land.relationships.some((r) => mine(r.source) || mine(r.target)) : null;\n const attested =\n touched === false && existsSync(paths.model) ? await attestedFrom(docsDir, paths, service, known) : [];\n const req = {\n service,\n expects,\n servicePath,\n attested,\n foreignTags: foreignTagsOf(elements),\n elementId: elements[0]?.id,\n // The one graded tag that changes what the brief may PROMISE: an `#external`\n // element is never `landscape.service-isolated`'s subject. Off the same\n // element `elementId` names, never the union.\n external: elements[0]?.tags.includes(EXTERNAL_TAG) ?? false,\n };\n const instruction = !modelled\n ? instructionFor({ ...req, state: \"unmodelled\" })\n : touched === false\n ? instructionFor({ ...req, state: \"edgeless\" })\n : null;\n return {\n present: true,\n parses: true,\n callersKnown: true,\n modelled,\n elements,\n inbound,\n outbound,\n expects,\n touched,\n attested,\n instruction,\n };\n}\n\n/**\n * The operations the fleet already calls on this service, read off the landscape\n * FILE alone — `null` when that file cannot be read either.\n *\n * The fallback for an `architecture/` project that does not parse, and the\n * narrowest one that can be justified: it answers ONE question, the one `loam\n * list` and `loam context` answer off the same bytes (`landscapeEvidence`,\n * core/vocabulary/maturity.ts). \"Nothing calls this service\" and \"loam could not\n * tell\" are different answers, and only the first may switch an API contract\n * off, which is why the empty list and the unreadable file are told apart here\n * rather than collapsed to `[]`.\n */\nasync function expectsFromFile(path: string, service: string, known: ReadonlySet<string>): Promise<string[] | null> {\n try {\n const doc = await loadFile(path);\n if (doc.errors.length > 0) return null;\n const svcOf = serviceResolver(doc.elements, known);\n const ops = doc.relationships\n .filter((r) => svcOf(r.target) === service)\n .map((r) => r.op)\n .filter((op): op is string => op !== undefined);\n return [...new Set(ops)];\n } catch {\n return null;\n }\n}\n"]}
1
+ {"version":3,"file":"landscape.js","sourceRoot":"","sources":["../../../src/core/brief/landscape.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The fleet-map half of the adoption brief, READ side: what the fleet map\n * already says about one service — its elements, its inbound and outbound\n * edges, the operations the fleet already calls on it — and which of the four\n * states the map is in for it. The write the service still owes the file — the\n * block, when nothing in it resolves to the service yet, or the EDGES, when an\n * element does and no edge touches it — is spelled in `./map/owed.ts`\n * (`landscapeArtifact`, `instructionFor`), which this module hands its findings\n * to. Both halves exist to stop an agent inventing a parallel model: the fleet\n * has already drawn some of these boxes and edges, and a baseline has to attach.\n *\n * \"The fleet map\" is the `architecture/` PROJECT — the landscape plus every\n * other `.likec4` under it, minus the generated views file — and not the\n * landscape FILE. The two are the same document set `validate --all` grades\n * `landscape.service-isolated` over, and reading one file here let the two\n * surfaces disagree about the same tree.\n */\nimport { existsSync } from \"node:fs\";\nimport { loadFile } from \"../c4/likec4.js\";\nimport { loadArchitecture } from \"../c4/project/architecture.js\";\nimport { attestedCalls, type AttestedCall } from \"../c4/resolve/attested.js\";\nimport { serviceResolver } from \"../c4/resolve/service.js\";\nimport { ServiceModels } from \"../c4/prefetch/fleet.js\";\nimport { OBLIGATION_TAG_PREFIX } from \"../obligations/obligations.js\";\nimport { landscapePath, type ServicePaths } from \"../repo/paths.js\";\nimport { enumeratedServiceIds, serviceTreePathOf } from \"../repo/service-target.js\";\nimport { EXTERNAL_TAG, GRADED_TAGS } from \"../vocabulary/maturity.js\";\nimport { brokenDocuments, instructionFor } from \"./map/owed.js\";\nimport { properAncestorIds } from \"../kernel/ids/fqn/ancestors.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\n\n/* ------------------------------------------------------------------ */\n/* What the fleet already says about this service */\n/* ------------------------------------------------------------------ */\n\nexport interface LandscapeElement {\n id: string;\n title: string;\n kind: string;\n tags: string[];\n /** True when the element binds explicitly with `metadata { service }`. */\n bound: boolean;\n}\n\nexport interface LandscapeEdge {\n from?: string;\n to?: string;\n op: string | null;\n title: string | null;\n}\n\nexport interface LandscapeContext {\n /** Whether `architecture/landscape.likec4` — the file the brief asks for edits to — is on disk. */\n present: boolean;\n /**\n * False when the landscape exists but the `architecture/` project does not\n * parse. A sibling under `architecture/` counts: a broken use case leaves the\n * map unusable for `validate --all` too, and a brief that read the landscape\n * alone would report a fleet nobody can render.\n */\n parses: boolean;\n /**\n * Whether loam could read WHO CALLS this service — the only evidence an EMPTY\n * `expects` may be read as \"nothing calls it\".\n *\n * A separate fact from `parses` because they answer about different documents.\n * `parses` is about the whole `architecture/` PROJECT (W4), and the callers\n * live in the landscape FILE: a broken palette or use case beside a landscape\n * that reads perfectly left `parses` false, and `apiExpected` then demanded an\n * OpenAPI contract of a browser UI the map proves nobody calls — while `loam\n * list` and `loam context`, which read the file (core/pack/living.ts), said the\n * opposite about the same tree.\n */\n callersKnown: boolean;\n /** Whether any element resolves to this service — null when nothing could be read. */\n modelled: boolean | null;\n /**\n * The service-LEVEL elements that resolve to this service: an element whose\n * ancestor also resolves to it is one of its containers, not a second box.\n */\n elements: LandscapeElement[];\n /**\n * The map's edges into and out of this service, INTERNAL edges excluded: an\n * edge whose two endpoints both resolve here (`svc.web -> svc.db`) is this\n * service's own wiring, and filed as inbound it named the service as its own\n * caller.\n */\n inbound: LandscapeEdge[];\n outbound: LandscapeEdge[];\n /** Operations other services already call on this one: the contract owes them. */\n expects: string[];\n /**\n * Whether at least one relationship in the map has an endpoint resolving to\n * this service — read off the relationships, NOT off `inbound`/`outbound`,\n * because an intra-service edge counts here (it is `landscape.service-\n * isolated`'s predicate) while those two lists drop it. `null` exactly when\n * `modelled` is `null` or `false`. It exists\n * because `instruction` used to go null on element existence alone, and\n * `loam seed` writes a bound, edgeless element for every service nobody\n * listed under `calls:` — so the brief declared the map finished for exactly\n * the services the map had not reached.\n *\n * Binary by design, and the fifth silent case of `landscape.service-isolated`\n * with it: ONE edge closes the state, so a map drawing one of five attested\n * calls reports `touched: true`, `attested: []` and no instruction. The check\n * is touched/untouched, not a set difference — loam cannot tell a call the map\n * omits from a call it deliberately does not draw at fleet level, and a\n * difference reported as an omission would be an invented edge with a code\n * beside it.\n */\n touched: boolean | null;\n /**\n * The calls `services/<…>/<svc>/model.likec4` declares ACROSS its boundary\n * (`attestedCalls`, core/c4/resolve/attested.ts). `[]` when the model is\n * absent, unparseable, or declares none — deliberately not told apart, since\n * the only thing the brief may do with the list is name edges ALREADY\n * ATTESTED, never invent one. Read only when `touched` is `false`: every\n * other state either has edges (nothing owed) or no element (the block, not\n * the edges, is owed), and a first adoption has no model to read anyway.\n */\n attested: AttestedCall[];\n /**\n * The write this service still owes the fleet map, in prose — null once an\n * element resolves to it AND an edge touches that element. It duplicates the\n * `landscape.likec4` target's shape rules on purpose: an agent driving off\n * `--json` reads `landscape` to learn what the fleet already says, and every\n * `targets[]` consumer walks the artifact list. Only one of those two readers\n * used to exist, so `adopt` briefed eight files and never once said the ninth\n * thing — that a documented service nobody drew is invisible to the whole\n * cross-service layer.\n */\n instruction: string | null;\n}\n\n/**\n * The tags loam reads on an ELEMENT. `GRADED_TAGS` is the vocabulary's own\n * list of the tags that change what loam concludes — spelled once there, so\n * the day a third tag is graded this set follows and the instruction below\n * does not tell an agent to clear it as unread. The obligation prefix is the\n * one tag PREFIX loam reads on an element; `#cap-`/`#req-` are view tags and\n * never sit on one, so they are not in this list.\n */\nconst LOAM_TAGS: ReadonlySet<string> = new Set(GRADED_TAGS);\n\n/**\n * The element's tags loam does NOT read — named in the instruction and never\n * interpreted. A placeholder convention (\"drawn ahead of adoption\") is the\n * fleet's own, `loam seed` writes none, and a view written `exclude\n * element.tag = #<that>` keeps an adopted service hidden until it is cleared.\n */\nfunction foreignTagsOf(elements: LandscapeElement[]): string[] {\n const unread = (t: string): boolean => !LOAM_TAGS.has(t) && !t.startsWith(OBLIGATION_TAG_PREFIX);\n return [...new Set(elements.flatMap((e) => e.tags))].filter(unread);\n}\n\n/**\n * The attested calls, or nothing off a model that could not be read: `c4.invalid`\n * is validate's finding, and an edge list read off half a document is invention.\n *\n * Through the ONE reader of `model.likec4` (`core/c4/service-model/load.ts`),\n * because the two shapes are read differently and this brief is handed to an\n * agent that will act on it: a model that EXTENDS the fleet map, opened as a\n * lone file, comes back as a pile of parse errors — so the brief would tell\n * every migrated service that its model attests nothing and quietly drop the\n * edges the map most likely owes. `mapUnreadable` is the same empty answer for\n * the same reason: an extending model whose map does not parse was never read.\n */\nasync function attestedFrom(\n docsDir: DocsDir,\n paths: ServicePaths,\n service: string,\n known: ReadonlySet<string>,\n): Promise<AttestedCall[]> {\n // The memo directly rather than through `core/fleet-context.ts`, and the\n // reason is the package graph: this module already depends on `core/c4/`, and\n // reaching for the read index would add a `brief -> core-root` edge for one\n // read in a command that grades a single service. The three things the memo\n // cannot know for itself are supplied here — the enumeration this function\n // was already handed, the per-file loader, and a counter nobody is keeping.\n const models = new ServiceModels({\n onProjectLoad: () => {\n // `adopt` reports no read counters, so there is nothing to increment. The\n // hook is the memo's way of letting a caller own its own statistics, and\n // an empty one is that answer, not a forgotten line.\n },\n known: async () => known,\n standalone: (path) => loadFile(path),\n });\n const model = await models.model(docsDir, paths);\n if (model.mapUnreadable || model.doc.errors.length > 0) return [];\n return attestedCalls(model.doc, service, known);\n}\n\n/**\n * What the living fleet map already says about this service — the half of the\n * brief that stops an agent inventing a parallel model: the fleet has already\n * drawn some of these boxes and edges, and the baseline has to attach to them.\n * An `architecture/` project that does not parse yields `modelled: null` —\n * \"nothing models it\" would be a claim about documents nobody could read; the\n * landscape FILE parsing on its own does not rescue it, because the map the\n * renderer and `validate --all` see is the whole project. `paths` is the\n * service's own artifact set, located by the caller through the enumeration\n * (never joined at `services/<id>/`): the model is opened in exactly one state —\n * an element with no edge — to name the calls the map most likely owes. The\n * WHOLE record travels rather than the model path alone, because the model is\n * now read through its own shape and an extending one is read as a project of\n * the service's directory plus the map. It is required rather than optional\n * because the edgeless instruction states what that file attests, and a caller\n * that omitted it would get a brief asserting a fact about a document loam never\n * opened.\n */\nexport async function landscapeContext(\n docsDir: DocsDir,\n service: string,\n paths: ServicePaths,\n): Promise<LandscapeContext> {\n const path = landscapePath(docsDir);\n // The service's own directory, spelled from the enumeration rather than joined\n // at the root: this text is handed to an AGENT that will go and edit files, and\n // a filed service named at `services/<id>/` sends it to create a second\n // directory beside the one that already exists.\n const servicePath = await serviceTreePathOf(docsDir, service);\n const bare = { service, expects: [], servicePath, attested: [], foreignTags: [] };\n const empty: LandscapeContext = {\n present: existsSync(path),\n parses: false,\n callersKnown: false,\n modelled: null,\n elements: [],\n inbound: [],\n outbound: [],\n expects: [],\n touched: null,\n attested: [],\n instruction: null,\n };\n if (!empty.present) return { ...empty, modelled: false, instruction: instructionFor({ ...bare, state: \"absent\" }) };\n\n // The `architecture/` PROJECT, through the one loader every other reader\n // shares — never `loadFile(path)`. A fleet spreads its model over more than\n // the landscape (a second `model { }` block in a sibling, a use case, a\n // palette), and `landscape.service-isolated` grades `touched` over exactly\n // this set: reading the landscape alone made `adopt` report `touched: false`,\n // the attested calls and an edit target for a service the fleet run on the\n // same tree called matched, with nothing to tell an agent which was right.\n //\n // `known`: the service ids the resolver may fall back on when an element\n // carries no `metadata { service }` binding — the same positive evidence\n // `validate` and `list` hand it. A docs repo with no `services/` yet (`adopt`\n // runs there) leaves the resolver with bindings alone, which is what it had\n // before.\n const known: ReadonlySet<string> = new Set(await enumeratedServiceIds(docsDir));\n const land = await loadArchitecture(docsDir);\n if (land.errors.length > 0) {\n // Since W4 this arm answers for the whole `architecture/` PROJECT, so the\n // document that broke is very often a SIBLING beside a landscape that parses\n // perfectly — and two things followed from not saying so:\n // - the instruction told an agent to go and fix parse errors in\n // `architecture/landscape.likec4`, which has none, contradicting\n // `validate --all` run one second later on the same tree;\n // - the API question — who CALLS this service — was answered \"unknown\"\n // off a landscape file loam can read, so a broken palette flipped\n // `openapi.yaml` to required for a browser UI the map proves nobody\n // calls, while `list` and `context` (which read the FILE, see\n // core/pack/living.ts) answered the opposite about the same tree.\n // Nothing else is rescued: `modelled`, `elements` and `touched` stay\n // unknown, because those are claims about the project the renderer loads.\n const expects = await expectsFromFile(path, service, known);\n return {\n ...empty,\n callersKnown: expects !== null,\n expects: expects ?? [],\n instruction: instructionFor({\n ...bare,\n expects: expects ?? [],\n state: \"unparseable\",\n broken: brokenDocuments(docsDir, land),\n }),\n };\n }\n\n // One resolver for the whole scan, built with the real service ids, and\n // built BEFORE the element filter: \"an element resolves to this service\" is\n // the resolver's answer — a nearest-ancestor binding wins over a descendant's\n // title — and it is the answer `validate --all` grades `service-unmodelled`\n // on and `loam context` prints `modelled` from. This used to ask each\n // element for its OWN binding-or-title instead, so a container titled\n // 'payment-service' inside an element bound to order-service read as\n // payment-service's box, and the edgeless arm told an agent \"do not add a\n // second element\" for a service the fleet run reported unmodelled (error).\n // The same resolver serves the edges: one drawn into a modelled container\n // (`paymentService.api`) is an edge into the service, and `expects` — which\n // decides whether this service owes an OpenAPI contract at all — has to see\n // it. Building it once also stops the per-edge rebuild the old\n // `serviceOf(land.elements, id)` call did.\n const svcOf = serviceResolver(land.elements, known);\n const mine = (id: string): boolean => svcOf(id) === service;\n // The service-LEVEL elements only: one that resolves to the service while\n // an ancestor does too is a container of it, and listing it would show a\n // second box the map does not have.\n const elements: LandscapeElement[] = land.elements\n .filter((e) => mine(e.id) && !properAncestorIds(e.id).some(mine))\n .map((e) => ({\n id: e.id,\n title: e.title,\n kind: e.kind,\n tags: e.tags,\n bound: e.service !== undefined,\n }));\n\n const inbound: LandscapeEdge[] = [];\n const outbound: LandscapeEdge[] = [];\n // An edge whose BOTH endpoints resolve here is the service's own internal\n // wiring: filed as inbound it made the service its own caller (verification\n // 2026-09-04). It still TOUCHES the service, so `touched` below reads the\n // relationships rather than these lists — the isolation check counts either\n // endpoint, and the two predicates must not disagree.\n const self = (r: { source: string; target: string }): boolean => mine(r.source) && mine(r.target);\n for (const r of land.relationships) {\n if (self(r)) continue;\n const edge = { op: r.op ?? null, title: r.title ?? null };\n if (mine(r.target)) inbound.push({ from: svcOf(r.source), ...edge });\n else if (mine(r.source)) outbound.push({ to: svcOf(r.target), ...edge });\n }\n\n const expects = [...new Set(inbound.map((e) => e.op).filter((op): op is string => op !== null))];\n const modelled = land.elements.some((e) => mine(e.id));\n const touched = modelled ? land.relationships.some((r) => mine(r.source) || mine(r.target)) : null;\n const attested =\n touched === false && existsSync(paths.model) ? await attestedFrom(docsDir, paths, service, known) : [];\n const req = {\n service,\n expects,\n servicePath,\n attested,\n foreignTags: foreignTagsOf(elements),\n elementId: elements[0]?.id,\n // The one graded tag that changes what the brief may PROMISE: an `#external`\n // element is never `landscape.service-isolated`'s subject. Off the same\n // element `elementId` names, never the union.\n external: elements[0]?.tags.includes(EXTERNAL_TAG) ?? false,\n };\n const instruction = !modelled\n ? instructionFor({ ...req, state: \"unmodelled\" })\n : touched === false\n ? instructionFor({ ...req, state: \"edgeless\" })\n : null;\n return {\n present: true,\n parses: true,\n callersKnown: true,\n modelled,\n elements,\n inbound,\n outbound,\n expects,\n touched,\n attested,\n instruction,\n };\n}\n\n/**\n * The operations the fleet already calls on this service, read off the landscape\n * FILE alone — `null` when that file cannot be read either.\n *\n * The fallback for an `architecture/` project that does not parse, and the\n * narrowest one that can be justified: it answers ONE question, the one `loam\n * list` and `loam context` answer off the same bytes (`landscapeEvidence`,\n * core/vocabulary/maturity.ts). \"Nothing calls this service\" and \"loam could not\n * tell\" are different answers, and only the first may switch an API contract\n * off, which is why the empty list and the unreadable file are told apart here\n * rather than collapsed to `[]`.\n */\nasync function expectsFromFile(path: string, service: string, known: ReadonlySet<string>): Promise<string[] | null> {\n try {\n const doc = await loadFile(path);\n if (doc.errors.length > 0) return null;\n const svcOf = serviceResolver(doc.elements, known);\n const ops = doc.relationships\n .filter((r) => svcOf(r.target) === service)\n .map((r) => r.op)\n .filter((op): op is string => op !== undefined);\n return [...new Set(ops)];\n } catch {\n return null;\n }\n}\n"]}
@@ -1,11 +1,14 @@
1
1
  /** The literals `targets.ts` leaves in the model target for this module to fill in. */
2
2
  export const FQN = "<fqn>";
3
3
  /**
4
- * The service being adopted, in the model example's header. It is a placeholder
5
- * for the same reason `<fqn>` is: the example is handed to an agent that copies
6
- * it, and a header naming payment-service travelled verbatim into every other
7
- * service's model — the more convincingly for sitting one line above an
8
- * `extend` the brief HAD substituted.
4
+ * The service being adopted, in the model example's header and in the spec
5
+ * examples' frontmatter and title. It is a placeholder for the same reason
6
+ * `<fqn>` is: the example is handed to an agent that copies it, and a header
7
+ * naming payment-service travelled verbatim into every other service's model —
8
+ * the more convincingly for sitting one line above an `extend` the brief HAD
9
+ * substituted. In spec.md and arch.spec.md the literal was worse than
10
+ * misleading: `service:` must name the directory the file sits in, so a
11
+ * verbatim paste failed `frontmatter.field-mismatch` everywhere but one service.
9
12
  */
10
13
  export const SVC = "<svc>";
11
14
  /** The token an example carries where loam has no id to substitute. Short on purpose — see `ExtendPoint`. */
@@ -1 +1 @@
1
- {"version":3,"file":"extend.js","sourceRoot":"","sources":["../../../../src/core/brief/map/extend.ts"],"names":[],"mappings":"AAiBA,uFAAuF;AACvF,MAAM,CAAC,MAAM,GAAG,GAAG,OAAO,CAAC;AAC3B;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,OAAO,CAAC;AAE3B,6GAA6G;AAC7G,MAAM,WAAW,GAAG,mBAAmB,CAAC;AAgBxC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,WAAW,CAAC,SAA2B;IACrD,IAAI,SAAS,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QAChC,OAAO;YACL,EAAE,EAAE,WAAW;YACf,IAAI,EACF,uGAAuG;gBACvG,uGAAuG;gBACvG,2DAA2D;SAC9D,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAChD,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC;IAC5B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO;YACL,EAAE,EAAE,WAAW;YACf,IAAI,EACF,8EAA8E;gBAC9E,4FAA4F;SAC/F,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;AAC1E,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,OAAe,EAAE,MAAmB,EAAE,OAAe;IAC9E,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACzE,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACrE,OAAO,GAAG,YAAY,CAAC,GAAG,MAAM,CAAC,EAAE,sBAAsB,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,IAAI,EAAE,CAAC;AACvF,CAAC;AAED,8FAA8F;AAC9F,SAAS,YAAY,CAAC,IAAY,EAAE,KAAK,GAAG,EAAE;IAC5C,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,IAAI,IAAI,GAAG,IAAI,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5C,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;YAC3D,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACf,IAAI,GAAG,MAAM,IAAI,EAAE,CAAC;QACtB,CAAC;;YAAM,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IAClC,CAAC;IACD,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC","sourcesContent":["/**\n * Which element of the fleet map an extending `model.likec4` says it is inside,\n * and what the brief's model example prints where the map has no answer.\n *\n * A module of its own beside `./owed.ts` for the reason that package exists: the\n * two are the fleet map's half of the brief, and `brief.ts` — which resolves the\n * artifact list against a docs repo — went past the file cap carrying the\n * substitution and its three-state reasoning. The subjects are distinct: this\n * one is about the ONE token an agent copies into three code positions, and\n * getting it wrong writes a model that cannot parse.\n *\n * Type-only import from `../` (`LandscapeContext`), which `verbatimModuleSyntax`\n * erases — so this package still value-imports nothing from its parent and the\n * package graph stays acyclic.\n */\nimport type { LandscapeContext } from \"../landscape.js\";\n\n/** The literals `targets.ts` leaves in the model target for this module to fill in. */\nexport const FQN = \"<fqn>\";\n/**\n * The service being adopted, in the model example's header. It is a placeholder\n * for the same reason `<fqn>` is: the example is handed to an agent that copies\n * it, and a header naming payment-service travelled verbatim into every other\n * service's model — the more convincingly for sitting one line above an\n * `extend` the brief HAD substituted.\n */\nexport const SVC = \"<svc>\";\n\n/** The token an example carries where loam has no id to substitute. Short on purpose — see `ExtendPoint`. */\nconst PLACEHOLDER = \"<your-element-id>\";\n\n/** What every `<fqn>` becomes, and the sentence that explains a placeholder. */\nexport interface ExtendPoint {\n id: string;\n /**\n * Why `id` is a placeholder, carried ONCE as a comment above the example\n * rather than substituted into it. `<fqn>` occupies three code positions —\n * the `extend` line, the source of a container edge, and `view of` — and the\n * explanation only reads as an instruction in the first: a 103-character\n * English sentence standing in for an id inside `view of … { include * }` is\n * noise the agent has to read past, on the command's most common invocation.\n */\n note?: string;\n}\n\n/**\n * Which element an extending `model.likec4` says it is inside — the id, when\n * the map leaves no doubt, and a placeholder plus the doubt when it does.\n *\n * Four answers because the map is in four states and only one of them has an\n * answer to give. With exactly ONE service-level element resolving to this\n * service, `extend <that id> {` is the file's first line and printing anything\n * else makes an agent go and look it up. With NONE, there is nothing to extend\n * yet — the landscape target below this one is what the agent must do first, and\n * saying so is what stops it inventing an id and writing a model that cannot\n * parse. With SEVERAL — `landscape.binding-duplicate`, a real and reported\n * state — loam must not choose: every element→service join in the fleet picks\n * one of them arbitrarily, so a brief that named one would be teaching an agent\n * the arbitrary answer as the right one. And when the map could not be READ\n * (`modelled === null`) the empty element list is evidence of nothing: \"write\n * the element into the map first\" is exactly the instruction that draws a second\n * box for a service the map already holds, so that arm says the map is\n * unreadable and asks for no map write at all.\n */\nexport function extendPoint(landscape: LandscapeContext): ExtendPoint {\n if (landscape.modelled === null) {\n return {\n id: PLACEHOLDER,\n note:\n \"the fleet map (architecture/) does not parse, so loam cannot say which element binds this directory. \" +\n \"Fix the map first — the landscape section of this brief names the documents that failed — then write \" +\n \"the id of the element that resolves to this service here.\",\n };\n }\n const ids = landscape.elements.map((e) => e.id);\n const [only, ...rest] = ids;\n if (only === undefined) {\n return {\n id: PLACEHOLDER,\n note:\n \"nothing in the fleet map resolves to this directory yet. Add the element to \" +\n \"architecture/landscape.likec4 first — the landscape target below — then write its id here.\",\n };\n }\n return { id: rest.length === 0 ? only : `<one of: ${ids.join(\", \")}>` };\n}\n\n/**\n * An example with its placeholders resolved, and the placeholder's explanation\n * prepended as a comment when there is one.\n *\n * Guarded on the example actually containing `<fqn>`: the note is about that\n * substitution, and heading an example that never had one with it would answer a\n * question its reader did not ask.\n */\nexport function exampleFor(example: string, extend: ExtendPoint, service: string): string {\n const body = example.replaceAll(FQN, extend.id).replaceAll(SVC, service);\n if (extend.note === undefined || !example.includes(FQN)) return body;\n return `${commentLines(`${extend.id} is a placeholder: ${extend.note}`)}\\n\\n${body}`;\n}\n\n/** One sentence as `//` lines, wrapped to the width the examples around it are written at. */\nfunction commentLines(text: string, width = 76): string {\n const out: string[] = [];\n let line = \"//\";\n for (const word of text.trim().split(/\\s+/)) {\n if (line !== \"//\" && line.length + 1 + word.length > width) {\n out.push(line);\n line = `// ${word}`;\n } else line = `${line} ${word}`;\n }\n out.push(line);\n return out.join(\"\\n\");\n}\n"]}
1
+ {"version":3,"file":"extend.js","sourceRoot":"","sources":["../../../../src/core/brief/map/extend.ts"],"names":[],"mappings":"AAiBA,uFAAuF;AACvF,MAAM,CAAC,MAAM,GAAG,GAAG,OAAO,CAAC;AAC3B;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,OAAO,CAAC;AAE3B,6GAA6G;AAC7G,MAAM,WAAW,GAAG,mBAAmB,CAAC;AAgBxC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,WAAW,CAAC,SAA2B;IACrD,IAAI,SAAS,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QAChC,OAAO;YACL,EAAE,EAAE,WAAW;YACf,IAAI,EACF,uGAAuG;gBACvG,uGAAuG;gBACvG,2DAA2D;SAC9D,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAChD,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC;IAC5B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO;YACL,EAAE,EAAE,WAAW;YACf,IAAI,EACF,8EAA8E;gBAC9E,4FAA4F;SAC/F,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;AAC1E,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,OAAe,EAAE,MAAmB,EAAE,OAAe;IAC9E,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACzE,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACrE,OAAO,GAAG,YAAY,CAAC,GAAG,MAAM,CAAC,EAAE,sBAAsB,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,IAAI,EAAE,CAAC;AACvF,CAAC;AAED,8FAA8F;AAC9F,SAAS,YAAY,CAAC,IAAY,EAAE,KAAK,GAAG,EAAE;IAC5C,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,IAAI,IAAI,GAAG,IAAI,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5C,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;YAC3D,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACf,IAAI,GAAG,MAAM,IAAI,EAAE,CAAC;QACtB,CAAC;;YAAM,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IAClC,CAAC;IACD,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC","sourcesContent":["/**\n * Which element of the fleet map an extending `model.likec4` says it is inside,\n * and what the brief's model example prints where the map has no answer.\n *\n * A module of its own beside `./owed.ts` for the reason that package exists: the\n * two are the fleet map's half of the brief, and `brief.ts` — which resolves the\n * artifact list against a docs repo — went past the file cap carrying the\n * substitution and its three-state reasoning. The subjects are distinct: this\n * one is about the ONE token an agent copies into three code positions, and\n * getting it wrong writes a model that cannot parse.\n *\n * Type-only import from `../` (`LandscapeContext`), which `verbatimModuleSyntax`\n * erases — so this package still value-imports nothing from its parent and the\n * package graph stays acyclic.\n */\nimport type { LandscapeContext } from \"../landscape.js\";\n\n/** The literals `targets.ts` leaves in the model target for this module to fill in. */\nexport const FQN = \"<fqn>\";\n/**\n * The service being adopted, in the model example's header and in the spec\n * examples' frontmatter and title. It is a placeholder for the same reason\n * `<fqn>` is: the example is handed to an agent that copies it, and a header\n * naming payment-service travelled verbatim into every other service's model —\n * the more convincingly for sitting one line above an `extend` the brief HAD\n * substituted. In spec.md and arch.spec.md the literal was worse than\n * misleading: `service:` must name the directory the file sits in, so a\n * verbatim paste failed `frontmatter.field-mismatch` everywhere but one service.\n */\nexport const SVC = \"<svc>\";\n\n/** The token an example carries where loam has no id to substitute. Short on purpose — see `ExtendPoint`. */\nconst PLACEHOLDER = \"<your-element-id>\";\n\n/** What every `<fqn>` becomes, and the sentence that explains a placeholder. */\nexport interface ExtendPoint {\n id: string;\n /**\n * Why `id` is a placeholder, carried ONCE as a comment above the example\n * rather than substituted into it. `<fqn>` occupies three code positions —\n * the `extend` line, the source of a container edge, and `view of` — and the\n * explanation only reads as an instruction in the first: a 103-character\n * English sentence standing in for an id inside `view of … { include * }` is\n * noise the agent has to read past, on the command's most common invocation.\n */\n note?: string;\n}\n\n/**\n * Which element an extending `model.likec4` says it is inside — the id, when\n * the map leaves no doubt, and a placeholder plus the doubt when it does.\n *\n * Four answers because the map is in four states and only one of them has an\n * answer to give. With exactly ONE service-level element resolving to this\n * service, `extend <that id> {` is the file's first line and printing anything\n * else makes an agent go and look it up. With NONE, there is nothing to extend\n * yet — the landscape target below this one is what the agent must do first, and\n * saying so is what stops it inventing an id and writing a model that cannot\n * parse. With SEVERAL — `landscape.binding-duplicate`, a real and reported\n * state — loam must not choose: every element→service join in the fleet picks\n * one of them arbitrarily, so a brief that named one would be teaching an agent\n * the arbitrary answer as the right one. And when the map could not be READ\n * (`modelled === null`) the empty element list is evidence of nothing: \"write\n * the element into the map first\" is exactly the instruction that draws a second\n * box for a service the map already holds, so that arm says the map is\n * unreadable and asks for no map write at all.\n */\nexport function extendPoint(landscape: LandscapeContext): ExtendPoint {\n if (landscape.modelled === null) {\n return {\n id: PLACEHOLDER,\n note:\n \"the fleet map (architecture/) does not parse, so loam cannot say which element binds this directory. \" +\n \"Fix the map first — the landscape section of this brief names the documents that failed — then write \" +\n \"the id of the element that resolves to this service here.\",\n };\n }\n const ids = landscape.elements.map((e) => e.id);\n const [only, ...rest] = ids;\n if (only === undefined) {\n return {\n id: PLACEHOLDER,\n note:\n \"nothing in the fleet map resolves to this directory yet. Add the element to \" +\n \"architecture/landscape.likec4 first — the landscape target below — then write its id here.\",\n };\n }\n return { id: rest.length === 0 ? only : `<one of: ${ids.join(\", \")}>` };\n}\n\n/**\n * An example with its placeholders resolved, and the placeholder's explanation\n * prepended as a comment when there is one.\n *\n * Guarded on the example actually containing `<fqn>`: the note is about that\n * substitution, and heading an example that never had one with it would answer a\n * question its reader did not ask.\n */\nexport function exampleFor(example: string, extend: ExtendPoint, service: string): string {\n const body = example.replaceAll(FQN, extend.id).replaceAll(SVC, service);\n if (extend.note === undefined || !example.includes(FQN)) return body;\n return `${commentLines(`${extend.id} is a placeholder: ${extend.note}`)}\\n\\n${body}`;\n}\n\n/** One sentence as `//` lines, wrapped to the width the examples around it are written at. */\nfunction commentLines(text: string, width = 76): string {\n const out: string[] = [];\n let line = \"//\";\n for (const word of text.trim().split(/\\s+/)) {\n if (line !== \"//\" && line.length + 1 + word.length > width) {\n out.push(line);\n line = `// ${word}`;\n } else line = `${line} ${word}`;\n }\n out.push(line);\n return out.join(\"\\n\");\n}\n"]}
@@ -18,25 +18,9 @@
18
18
  * `import type { BriefTarget }`, which `verbatimModuleSyntax` erases, so this
19
19
  * package value-imports nothing from `../` and the package graph stays acyclic.
20
20
  */
21
- import { relative } from "node:path";
21
+ import { brokenDocumentPaths } from "../../c4/likec4.js";
22
22
  import { spellCall, edgeTemplates } from "../../c4/resolve/attested.js";
23
- /**
24
- * The LikeC4 identifier the fleet map's element for a service conventionally
25
- * carries: the directory name, camel-cased. A suggestion and nothing more —
26
- * `metadata { service '<id>' }` is what joins the element to the directory —
27
- * but a brief handing over a concrete block gets followed, and one describing
28
- * a block in prose gets improvised on.
29
- */
30
- function elementIdFor(service) {
31
- const parts = service.split(/[^A-Za-z0-9]+/).filter((p) => p !== "");
32
- if (parts.length === 0)
33
- return "service";
34
- const head = parts[0].toLowerCase();
35
- const tail = parts.slice(1).map((p) => p[0].toUpperCase() + p.slice(1).toLowerCase());
36
- const id = head + tail.join("");
37
- // LikeC4 identifiers cannot open with a digit; a service id can.
38
- return /^[0-9]/.test(id) ? `svc${id[0].toUpperCase()}${id.slice(1)}` : id;
39
- }
23
+ import { serviceElementId } from "../../c4/seed/template.js";
40
24
  /**
41
25
  * The write this service still owes `architecture/landscape.likec4`.
42
26
  *
@@ -68,7 +52,7 @@ function landscapeBlock(req) {
68
52
  "}",
69
53
  ].join("\n");
70
54
  }
71
- const id = elementIdFor(service);
55
+ const id = serviceElementId(service);
72
56
  const calls = expects.length > 0
73
57
  ? expects.map((op) => `// <caller> -> ${id} 'Calls ${op}' { metadata { op '${op}' } }`)
74
58
  : [`// <caller> -> ${id} 'Calls <operationId>' { metadata { op '<operationId>' } }`];
@@ -148,13 +132,11 @@ export function landscapeArtifact(req) {
148
132
  * Spelled exactly as `commands/validate/fleet/landscape.ts` spells it for
149
133
  * `landscape.invalid` — deduped, relative to the docs root, `/`-joined — because
150
134
  * the two surfaces are read one after the other on the same tree and an agent
151
- * has to be able to tell that they name the same file.
135
+ * has to be able to tell that they name the same file: both go through
136
+ * `c4/likec4.ts`'s `brokenDocumentPaths`.
152
137
  */
153
138
  export function brokenDocuments(docsDir, land) {
154
- const paths = [...new Set(land.errors.map((e) => e.sourceFsPath).filter((p) => p !== undefined))]
155
- .map((abs) => relative(docsDir, abs).split(/[\\/]/).join("/"))
156
- .sort();
157
- return { paths, errors: land.errors.length };
139
+ return { paths: brokenDocumentPaths(docsDir, land.errors), errors: land.errors.length };
158
140
  }
159
141
  /**
160
142
  * The fleet-map instruction, in the four states a landscape can be in. It says
@@ -165,7 +147,7 @@ export function brokenDocuments(docsDir, land) {
165
147
  */
166
148
  export function instructionFor(req) {
167
149
  const { service, state, expects, servicePath } = req;
168
- const id = elementIdFor(service);
150
+ const id = serviceElementId(service);
169
151
  const bind = `an element bound with \`metadata { service '${service}' }\``;
170
152
  const consequence = `Until one resolves, \`loam validate --all\` reports \`landscape.service-unmodelled\` (error) for ` +
171
153
  `${servicePath}/: the service is documented and invisible — no edge into it can be checked ` +
@@ -244,7 +226,7 @@ function accountability(req, id) {
244
226
  }
245
227
  function edgelessInstruction(req) {
246
228
  const { servicePath, attested, foreignTags } = req;
247
- const id = req.elementId ?? elementIdFor(req.service);
229
+ const id = req.elementId ?? serviceElementId(req.service);
248
230
  // "the fleet map (architecture/)" rather than the landscape FILE: the map is
249
231
  // the whole project, an element may be declared in any document of it, and
250
232
  // LikeC4 hands back no source document for an element — so naming a file
@@ -1 +1 @@
1
- {"version":3,"file":"owed.js","sourceRoot":"","sources":["../../../../src/core/brief/map/owed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAqB,MAAM,8BAA8B,CAAC;AAG3F;;;;;;GAMG;AACH,SAAS,YAAY,CAAC,OAAe;IACnC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IACrE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACzC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IACvF,MAAM,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChC,iEAAiE;IACjE,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC7E,CAAC;AAyBD;;;;;;;;;;GAUG;AACH,SAAS,cAAc,CAAC,GAA6B;IACnD,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC;IAC1C,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,EAAE,GAAG,GAAG,CAAC,SAAS,CAAC;QACzB,OAAO;YACL,SAAS;YACT,mDAAmD,EAAE,wBAAwB,GAAG,CAAC,WAAW,OAAO;YACnG,EAAE;YACF,0EAA0E;YAC1E,UAAU,EAAE,wEAAwE;YACpF,sBAAsB,EAAE,4DAA4D;YACpF,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;gBACzB,CAAC,CAAC;oBACE,kBAAkB,GAAG,CAAC,WAAW,qEAAqE;oBACtG,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,EAAE,IAAI,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC3D;gBACH,CAAC,CAAC,EAAE,CAAC;YACP,GAAG;SACJ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IACD,MAAM,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACjC,MAAM,KAAK,GACT,OAAO,CAAC,MAAM,GAAG,CAAC;QAChB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,oBAAoB,EAAE,WAAW,EAAE,sBAAsB,EAAE,OAAO,CAAC;QACzF,CAAC,CAAC,CAAC,oBAAoB,EAAE,4DAA4D,CAAC,CAAC;IAC3F,MAAM,KAAK,GAAG;QACZ,SAAS;QACT,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,kEAAkE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5F,KAAK,EAAE,sBAAsB,OAAO,KAAK;QACzC,oEAAoE;QACpE,2BAA2B,OAAO,KAAK;QACvC,KAAK;QACL,EAAE;QACF,iDAAiD;QACjD,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;QACnC,GAAG;KACJ,CAAC;IACF,4EAA4E;IAC5E,0EAA0E;IAC1E,+EAA+E;IAC/E,kEAAkE;IAClE,yDAAyD;IACzD,OAAO,OAAO;QACZ,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QAClB,CAAC,CAAC,CAAC,iBAAiB,EAAE,0BAA0B,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpF,CAAC;AAED,MAAM,YAAY,GAChB,mRAAmR,CAAC;AAEtR,kFAAkF;AAClF,MAAM,aAAa,GAAG,CAAC,WAAmB,EAAU,EAAE,CACpD,2GAA2G;IAC3G,KAAK,WAAW,6FAA6F;IAC7G,kFAAkF,CAAC;AAErF;;;;;;;;;;GAUG;AACH,MAAM,aAAa,GAAG,CAAC,SAAiB,EAAE,WAAmB,EAAU,EAAE,CACvE,KAAK,SAAS,+FAA+F;IAC7G,2GAA2G;IAC3G,6GAA6G;IAC7G,cAAc,WAAW,sFAAsF;IAC/G,+FAA+F,CAAC;AAElG,sGAAsG;AACtG,MAAM,UAAU,iBAAiB,CAAC,GAA6B;IAC7D,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC;IAChD,MAAM,KAAK,GACT,yNAAyN,CAAC;IAC5N,OAAO;QACL,QAAQ,EAAE,kBAAkB;QAC5B,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE,mBACP,SAAS,KAAK,SAAS;YACrB,CAAC,CAAC,6EAA6E;YAC/E,CAAC,CAAC,4FACN,6CAA6C;QAC7C,KAAK,EACH,SAAS,KAAK,SAAS;YACrB,CAAC,CAAC;gBACE,KAAK;gBACL,4FAA4F,OAAO,yCAAyC,WAAW,gFAAgF;gBACvO,6PAA6P;gBAC7P,YAAY;gBACZ,0PAA0P;aAC3P;YACH,CAAC,CAAC;gBACE,KAAK;gBACL,oCAAoC,WAAW,UAAU,SAAS,uOAAuO;gBACzS,YAAY;gBACZ,GAAG,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,WAAW,CAAC;aAC3F;QACP,OAAO,EAAE,cAAc,CAAC,GAAG,CAAC;KAC7B,CAAC;AACJ,CAAC;AAaD;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,IAAwC;IACvF,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC;SAC3G,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;SAC7D,IAAI,EAAE,CAAC;IACV,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;AAC/C,CAAC;AAsBD;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,GAAuB;IACpD,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC;IACrD,MAAM,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,+CAA+C,OAAO,OAAO,CAAC;IAC3E,MAAM,WAAW,GACf,mGAAmG;QACnG,GAAG,WAAW,8EAA8E;QAC5F,iEAAiE,CAAC;IACpE,6EAA6E;IAC7E,wEAAwE;IACxE,4EAA4E;IAC5E,6EAA6E;IAC7E,0EAA0E;IAC1E,uEAAuE;IACvE,6EAA6E;IAC7E,8DAA8D;IAC9D,MAAM,IAAI,GACR,OAAO,CAAC,MAAM,GAAG,CAAC;QAChB,CAAC,CAAC,4BAA4B,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,oFAAoF;QACzJ,CAAC,CAAC,EAAE,CAAC;IAET,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,OAAO,CACL,qGAAqG;YACrG,wEAAwE,IAAI,GAAG;YAC/E,qBAAqB,EAAE,sBAAsB,OAAO,mDAAmD;YACvG,iDAAiD,WAAW,GAAG,IAAI,EAAE,CACtE,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,KAAK,aAAa,EAAE,CAAC;QAC5B,2EAA2E;QAC3E,0EAA0E;QAC1E,2EAA2E;QAC3E,kEAAkE;QAClE,wEAAwE;QACxE,6EAA6E;QAC7E,sCAAsC;QACtC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC;QACrB,MAAM,KAAK,GACT,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YACrC,CAAC,CAAC,8CAA8C;YAChD,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,eAAe,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW;gBACvG,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,oDAAoD,CAAC;QACzG,OAAO,CACL,GAAG,KAAK,gGAAgG;YACxG,wBAAwB,WAAW,uEAAuE;YAC1G,wGAAwG;YACxG,4DAA4D,OAAO,0CAA0C;YAC7G,SAAS,IAAI,sFAAsF;YACnG,oCAAoC,CACrC,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,KAAK,UAAU;QAAE,OAAO,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAC1D,OAAO,CACL,wDAAwD,WAAW,uCAAuC;QAC1G,+CAA+C,IAAI,mBAAmB;QACtE,KAAK,EAAE,sBAAsB,OAAO,kEAAkE;QACtG,+EAA+E,WAAW,GAAG,IAAI,EAAE,CACpG,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH;;;;;;;;;GASG;AACH,SAAS,cAAc,CAAC,GAAuB,EAAE,EAAU;IACzD,IAAI,GAAG,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC1B,OAAO,CACL,KAAK,EAAE,qGAAqG;YAC5G,2GAA2G;YAC3G,8FAA8F;YAC9F,GAAG,GAAG,CAAC,WAAW,yCAAyC,CAC5D,CAAC;IACJ,CAAC;IACD,OAAO,CACL,2GAA2G;QAC3G,0GAA0G;QAC1G,UAAU,CACX,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,GAAuB;IAClD,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC;IACnD,MAAM,EAAE,GAAG,GAAG,CAAC,SAAS,IAAI,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACtD,6EAA6E;IAC7E,2EAA2E;IAC3E,yEAAyE;IACzE,6EAA6E;IAC7E,mEAAmE;IACnE,MAAM,OAAO,GACX,KAAK,EAAE,mDAAmD,WAAW,wCAAwC;QAC7G,kEAAkE,CAAC;IACrE,MAAM,IAAI,GACR,WAAW,CAAC,MAAM,KAAK,CAAC;QACtB,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,wBAAwB,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,6CAA6C;YAC/G,gGAAgG;YAChG,oEAAoE,CAAC;IAC3E,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,yEAAyE;QACzE,wEAAwE;QACxE,gBAAgB;QAChB,MAAM,KAAK,GAAG,aAAa,CAAC,EAAE,EAAE,QAAQ,CAAC;aACtC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;aACtB,IAAI,CAAC,MAAM,CAAC,CAAC;QAChB,OAAO,CACL,GAAG,OAAO,IAAI,WAAW,kCAAkC,QAAQ,CAAC,MAAM,gCAAgC;YAC1G,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,wCAAwC,EAAE,uBAAuB;YACtG,aAAa,KAAK,0CAA0C;YAC5D,qGAAqG;YACrG,0GAA0G;YAC1G,yBAAyB,cAAc,CAAC,GAAG,EAAE,EAAE,CAAC,2DAA2D;YAC3G,uBAAuB,IAAI,EAAE,CAC9B,CAAC;IACJ,CAAC;IACD,OAAO,CACL,GAAG,OAAO,eAAe,WAAW,yEAAyE;QAC7G,4GAA4G;QAC5G,qFAAqF,EAAE,cAAc;QACrG,4GAA4G;QAC5G,6GAA6G;QAC7G,8GAA8G;QAC9G,8GAA8G;QAC9G,oBAAoB,IAAI,EAAE,CAC3B,CAAC;AACJ,CAAC","sourcesContent":["/**\n * The write one service still owes `architecture/landscape.likec4`: the\n * target the brief hands over (`landscapeArtifact` — its shape rules and the\n * example block or edges) and the instruction in prose (`instructionFor` —\n * the file, the block, and the consequence, in the four states a landscape\n * can be in). Both exist to stop an agent inventing a parallel model: the\n * fleet has already drawn some of these boxes and edges, and a baseline has to\n * attach.\n *\n * A sub-package of `core/brief/` because that package sits at its five-file\n * cap and `landscape.ts` — which READS what the map already says about the\n * service and decides which state it is in — had parked at the file ceiling\n * with its object literals collapsed onto single lines to fit. The two halves\n * are distinct subjects: one is the derivation over a parsed document, the\n * other is all of the prose plus the vocabulary that prose reads (`BrokenMap`\n * and its builder live here for that reason — beside the arm that spends them,\n * not in the caller that fills them). The only tie back to the parent is\n * `import type { BriefTarget }`, which `verbatimModuleSyntax` erases, so this\n * package value-imports nothing from `../` and the package graph stays acyclic.\n */\nimport { relative } from \"node:path\";\nimport type { LikeC4Error } from \"../../c4/likec4.js\";\nimport { spellCall, edgeTemplates, type AttestedCall } from \"../../c4/resolve/attested.js\";\nimport type { BriefTarget } from \"../targets.js\";\n\n/**\n * The LikeC4 identifier the fleet map's element for a service conventionally\n * carries: the directory name, camel-cased. A suggestion and nothing more —\n * `metadata { service '<id>' }` is what joins the element to the directory —\n * but a brief handing over a concrete block gets followed, and one describing\n * a block in prose gets improvised on.\n */\nfunction elementIdFor(service: string): string {\n const parts = service.split(/[^A-Za-z0-9]+/).filter((p) => p !== \"\");\n if (parts.length === 0) return \"service\";\n const head = parts[0]!.toLowerCase();\n const tail = parts.slice(1).map((p) => p[0]!.toUpperCase() + p.slice(1).toLowerCase());\n const id = head + tail.join(\"\");\n // LikeC4 identifiers cannot open with a digit; a service id can.\n return /^[0-9]/.test(id) ? `svc${id[0]!.toUpperCase()}${id.slice(1)}` : id;\n}\n\n/* ------------------------------------------------------------------ */\n/* The target */\n/* ------------------------------------------------------------------ */\n\n/** What `landscapeArtifact` needs — one record, because the fields only ever travel together. */\nexport interface LandscapeArtifactRequest {\n service: string;\n expects: string[];\n present: boolean;\n servicePath: string;\n /** The element that already resolves to the service — set only in the edgeless state — and the calls its model attests. */\n elementId?: string;\n /**\n * Whether that element carries `#external`. It changes what the brief may\n * PROMISE and nothing else: `landscape.service-isolated` removes every\n * `#external` element from its subject set, so on such a tree no warning holds\n * the agent accountable, and saying one will is the false promise this flag\n * exists to stop (verification 2026-09-04).\n */\n external?: boolean;\n attested: AttestedCall[];\n}\n\n/**\n * The write this service still owes `architecture/landscape.likec4`.\n *\n * `expects` is what the fleet ALREADY calls on the service — those edges exist\n * in the map today, so the example draws the outbound side as a comment rather\n * than telling an agent to duplicate an edge that is already there. With\n * `elementId` set the example is EDGES ONLY, on the element that exists: a\n * block here would be the second box the edgeless state exists to refuse, and\n * the attested calls are listed as comments the agent turns into edges by\n * naming the other party as the map spells it.\n */\nfunction landscapeBlock(req: LandscapeArtifactRequest): string {\n const { service, expects, present } = req;\n if (req.elementId !== undefined) {\n const id = req.elementId;\n return [\n \"model {\",\n ` // ... every element stays exactly as it is — ${id} already resolves to ${req.servicePath}/ ...`,\n \"\",\n \" // every call in or out of it, one edge each, on the existing element:\",\n ` // ${id} -> <callee> 'Calls <operationId>' { metadata { op '<operationId>' } }`,\n ` // <caller> -> ${id} 'Calls <operationId>' { metadata { op '<operationId>' } }`,\n ...(req.attested.length > 0\n ? [\n ` // the calls ${req.servicePath}/model.likec4 already attests, as the model spells the other party:`,\n ...req.attested.map((c) => ` // ${id} ${spellCall(c)}`),\n ]\n : []),\n \"}\",\n ].join(\"\\n\");\n }\n const id = elementIdFor(service);\n const calls =\n expects.length > 0\n ? expects.map((op) => `// <caller> -> ${id} 'Calls ${op}' { metadata { op '${op}' } }`)\n : [`// <caller> -> ${id} 'Calls <operationId>' { metadata { op '<operationId>' } }`];\n const model = [\n \"model {\",\n ...(present ? [\" // ... the fleet's other services stay exactly as they are ...\", \"\"] : []),\n ` ${id} = softwareSystem '${service}' {`,\n \" description '<one line: what this service is responsible for>'\",\n ` metadata { service '${service}' }`,\n \" }\",\n \"\",\n \" // every call in or out of it, one edge each:\",\n ...calls.map((line) => ` ${line}`),\n \"}\",\n ];\n // When the file does not exist the example is the WHOLE file, so it carries\n // the `specification` block a bare `model` would be rejected without: the\n // brief must never hand over a document `loam validate` refuses. When the file\n // does exist the example is a fragment to splice, and repeating a\n // `specification` block there would invite a second one.\n return present\n ? model.join(\"\\n\")\n : [\"specification {\", \" element softwareSystem\", \"}\", \"\", ...model].join(\"\\n\");\n}\n\nconst OP_EDGE_RULE =\n \"Draw every cross-service call as an edge carrying the operation it uses: `a -> b 'Calls createSplit' { metadata { op 'createSplit' } }`. The `op` must be an operationId the TARGET's openapi.yaml defines, or `spine.op-undefined` (error) — a broken contract between services.\";\n\n/** What the edgeless state costs when a check is watching — the ordinary case. */\nconst ISOLATED_RULE = (servicePath: string): string =>\n `\\`loam validate --all\\` reports \\`landscape.service-isolated\\` (warn) while this element has no edge and ` +\n `\\`${servicePath}/model.likec4\\` declares a call across its boundary; with no such call it stays silent, so ` +\n `an edgeless service that truly calls nothing is correct, and this brief says so.`;\n\n/**\n * The same state under `#external`, where NO check is watching.\n *\n * `landscape.service-isolated` removes every `#external` element from its\n * subject set — the fix that stopped a foreign box answering for one of our\n * directories — so on this tree the warning the brief used to promise cannot\n * fire, measured: `validate --all` reports `landscape.matched` and the full\n * `--json` findings walk holds no isolation finding at all. A brief that names\n * a warning nothing will raise is worse than one that says nothing, because the\n * agent budgets against the check instead of against the sentence.\n */\nconst EXTERNAL_RULE = (elementId: string, servicePath: string): string =>\n `\\`${elementId}\\` carries \\`#external\\`, so \\`landscape.service-isolated\\` will NOT fire here whatever this ` +\n `element's edges are — the check skips every element the map marks as somebody else's, exactly as the two ` +\n `binding checks do. Nothing in \\`loam validate --all\\` names this state; this brief is the only place it is ` +\n `said. If \\`${servicePath}/\\` is ours, drop \\`#external\\` from the element and the check applies again; if it ` +\n `is genuinely somebody else's, the calls belong on the element of the service that makes them.`;\n\n/** The fleet map's own brief — assembled per service, because the write it owes names the service. */\nexport function landscapeArtifact(req: LandscapeArtifactRequest): Omit<BriefTarget, \"path\" | \"exists\" | \"action\"> {\n const { service, servicePath, elementId } = req;\n const whole =\n \"This file is the WHOLE FLEET's, not this service's. Add to it; never rewrite it. Everything already in `model { ... }` belongs to the other services, and replacing the file destroys their map along with their edges.\";\n return {\n artifact: \"landscape.likec4\",\n required: true,\n purpose: `the fleet map — ${\n elementId === undefined\n ? \"until an element here resolves to this directory, the service is documented\"\n : \"an element here resolves to this directory and no edge touches it, so the service is drawn\"\n } and invisible to every cross-service check`,\n shape:\n elementId === undefined\n ? [\n whole,\n `Add one top-level element for the service, bound to its directory: \\`metadata { service '${service}' }\\`. Until an element resolves to \\`${servicePath}/\\`, \\`loam validate --all\\` reports \\`landscape.service-unmodelled\\` (error).`,\n \"Bind rather than rename: a binding whose id names no directory is `landscape.binding-unknown` (error), and two elements binding the SAME directory is `landscape.binding-duplicate` (warn) — every element→service join then picks one of them arbitrarily.\",\n OP_EDGE_RULE,\n \"If the file does not exist yet, create it with a `specification { ... }` block declaring the kinds you use, then the `model { ... }` block. A landscape that does not parse is `landscape.invalid` (error) and blinds every cross-service check at once.\",\n ]\n : [\n whole,\n `An element already resolves to \\`${servicePath}/\\` (\\`${elementId}\\`). Do NOT add a second one — two elements resolving to one directory is \\`landscape.binding-duplicate\\` (warn) and every element→service join then picks one of them arbitrarily. What the map lacks is every call in or out of it.`,\n OP_EDGE_RULE,\n req.external === true ? EXTERNAL_RULE(elementId, servicePath) : ISOLATED_RULE(servicePath),\n ],\n example: landscapeBlock(req),\n };\n}\n\n/* ------------------------------------------------------------------ */\n/* The instruction */\n/* ------------------------------------------------------------------ */\n\n/** Which documents of `architecture/` failed, and how many errors they carry between them. */\nexport interface BrokenMap {\n /** Docs-relative POSIX paths, deduped and sorted — spelled as `landscape.invalid` spells them. */\n paths: string[];\n errors: number;\n}\n\n/**\n * That record, built from a project's diagnostics — beside the type it fills and\n * the arm that reads it, rather than in the caller.\n *\n * Spelled exactly as `commands/validate/fleet/landscape.ts` spells it for\n * `landscape.invalid` — deduped, relative to the docs root, `/`-joined — because\n * the two surfaces are read one after the other on the same tree and an agent\n * has to be able to tell that they name the same file.\n */\nexport function brokenDocuments(docsDir: string, land: { errors: readonly LikeC4Error[] }): BrokenMap {\n const paths = [...new Set(land.errors.map((e) => e.sourceFsPath).filter((p): p is string => p !== undefined))]\n .map((abs) => relative(docsDir, abs).split(/[\\\\/]/).join(\"/\"))\n .sort();\n return { paths, errors: land.errors.length };\n}\n\n/** The inputs of `instructionFor` — a record, because the function sat at the parameter cap. */\nexport interface InstructionRequest {\n service: string;\n state: \"absent\" | \"unparseable\" | \"unmodelled\" | \"edgeless\";\n expects: string[];\n servicePath: string;\n /**\n * The documents that failed (`unparseable` state only). Optional so the other\n * three arms need not carry it, and the arm degrades to naming the project\n * rather than guessing a file when a caller has none.\n */\n broken?: BrokenMap;\n /** The element that already resolves to the service (edgeless state only), and its tags loam does not read. */\n elementId?: string;\n /** Whether that element carries `#external` — see `LandscapeArtifactRequest.external`. */\n external?: boolean;\n attested: AttestedCall[];\n foreignTags: string[];\n}\n\n/**\n * The fleet-map instruction, in the four states a landscape can be in. It says\n * the file, the block, and the consequence, in that order. The consequence is\n * the part agents acted on: \"add an element\" reads as tidiness, \"until you do,\n * `loam validate --all` fails and no cross-service check can see this service\"\n * reads as work.\n */\nexport function instructionFor(req: InstructionRequest): string {\n const { service, state, expects, servicePath } = req;\n const id = elementIdFor(service);\n const bind = `an element bound with \\`metadata { service '${service}' }\\``;\n const consequence =\n `Until one resolves, \\`loam validate --all\\` reports \\`landscape.service-unmodelled\\` (error) for ` +\n `${servicePath}/: the service is documented and invisible — no edge into it can be checked ` +\n `against its openapi.yaml, and no feature can draw a call to it.`;\n // `expects` is reachable only through inbound edges, so it is empty in every\n // state that ASKS for an element — the edgeless one by definition. (The\n // unparseable state can carry it, off the landscape-file fallback, and that\n // arm deliberately does not append this sentence: it asks for no element, so\n // \"your element is what they will resolve to\" would name one loam has not\n // read.) It is threaded through anyway rather than dropped: the caller\n // computes it once, and the day an unmodelled service can inherit an edge (a\n // container-level binding, say) the sentence is already here.\n const owed =\n expects.length > 0\n ? ` The fleet already calls ${expects.map((o) => `'${o}'`).join(\", \")} on this service, so those edges exist; your element is what they will resolve to.`\n : \"\";\n\n if (state === \"absent\") {\n return (\n `architecture/landscape.likec4 does not exist yet. Create it with a \\`specification { ... }\\` block ` +\n `declaring the kinds you use and a \\`model { ... }\\` block containing ${bind} ` +\n `(conventionally \\`${id} = softwareSystem '${service}'\\`), plus one edge per cross-service call, each ` +\n `carrying \\`metadata { op '<operationId>' }\\`. ${consequence}${owed}`\n );\n }\n if (state === \"unparseable\") {\n // The map is the whole `architecture/` project, so the document that broke\n // is usually a SIBLING of the landscape — a use case, a palette. This arm\n // named `architecture/landscape.likec4` regardless and told an agent to go\n // and fix parse errors in a file that has none, contradicting the\n // `landscape.invalid` that `validate --all` prints on the same tree one\n // second later. The EDGELESS arm below carries the same rule in its own WHY:\n // never name bytes loam did not read.\n const b = req.broken;\n const named =\n b === undefined || b.paths.length === 0\n ? \"The fleet map (architecture/) does not parse\"\n : `${b.paths.join(\", \")} ${b.paths.length === 1 ? \"does\" : \"do\"} not parse (${String(b.errors)} error(s)` +\n `${b.paths.length === 1 ? \"\" : \" between them\"}), so the fleet map (architecture/) cannot be read`;\n return (\n `${named} (\\`landscape.invalid\\`). Nothing can be said about what the map already models — not that it ` +\n `lacks an element for ${servicePath}/, and not that it holds one — so this brief asks for no edit to it. ` +\n `Fix the parse errors first (they blind every cross-service check at once, and \\`loam validate --all\\` ` +\n `names the same file), then re-run \\`loam adopt --service ${service}\\`: it will say whether the map already ` +\n `holds ${bind}, and adding one before then risks a second box for a service the map already draws ` +\n `(\\`landscape.binding-duplicate\\`).`\n );\n }\n if (state === \"edgeless\") return edgelessInstruction(req);\n return (\n `Nothing in architecture/landscape.likec4 resolves to ${servicePath}/. ADD to that file — do not rewrite ` +\n `it, every other service's map is in there — ${bind}, conventionally ` +\n `\\`${id} = softwareSystem '${service}'\\`, inside the existing \\`model { ... }\\` block, and draw each ` +\n `cross-service call as an edge carrying \\`metadata { op '<operationId>' }\\`. ${consequence}${owed}`\n );\n}\n\n/**\n * The fourth state, in two arms: with attested calls it names each and says to\n * carry it up as ONE edge on the existing element; without, it points at the\n * walk stops that produce the evidence and otherwise says draw NOTHING — an\n * invented edge is a dependency the fleet then plans against. Neither arm asks\n * for a second element, and both name the tags loam does not read.\n */\n/**\n * Which sentence the attested arm may end on: what will hold the agent to this\n * edit, or the admission that nothing will.\n *\n * `landscape.service-isolated` skips every `#external` element, so on such a\n * tree the promise \"reports it until an edge lands\" is one the check cannot\n * keep — and the brief was making it on exactly the tree that fix was written\n * for (verification 2026-09-04). The `#external` arm says so instead, and says\n * how to make the check apply.\n */\nfunction accountability(req: InstructionRequest, id: string): string {\n if (req.external === true) {\n return (\n `\\`${id}\\` carries \\`#external\\`, so \\`landscape.service-isolated\\` will NOT fire here whatever you draw — ` +\n `the check skips every element the map marks as somebody else's. Nothing in \\`loam validate --all\\` names ` +\n `this state; this brief is the only place it is said. Drop \\`#external\\` from the element if ` +\n `${req.servicePath}/ is ours, and the check applies again.`\n );\n }\n return (\n \"`loam validate --all` reports `landscape.service-isolated` (warn) until an edge lands — ONE edge, on any \" +\n \"of them: the check is touched/untouched, not a set difference, so nothing will name the calls you leave \" +\n \"undrawn.\"\n );\n}\n\nfunction edgelessInstruction(req: InstructionRequest): string {\n const { servicePath, attested, foreignTags } = req;\n const id = req.elementId ?? elementIdFor(req.service);\n // \"the fleet map (architecture/)\" rather than the landscape FILE: the map is\n // the whole project, an element may be declared in any document of it, and\n // LikeC4 hands back no source document for an element — so naming a file\n // here would be a claim about bytes loam did not read. The write still lands\n // in `architecture/landscape.likec4`; the target's `path` says so.\n const opening =\n `\\`${id}\\` in the fleet map (architecture/) resolves to ${servicePath}/ and no edge in the map touches it — ` +\n `the service is drawn and invisible to every cross-service check.`;\n const tags =\n foreignTags.length === 0\n ? \"\"\n : ` The element carries ${foreignTags.map((t) => `#${t}`).join(\", \")} — tags loam does not read; if one marks a ` +\n `placeholder drawn ahead of adoption, clearing it is part of this edit, because a view written ` +\n `\\`exclude element.tag = #<that>\\` keeps an adopted service hidden.`;\n if (attested.length > 0) {\n // The edge form follows the calls' direction (`edgeTemplates`): this arm\n // used to spell the outbound form alone, so an inbound call was carried\n // up backwards.\n const forms = edgeTemplates(id, attested)\n .map((t) => `\\`${t}\\``)\n .join(\" or \");\n return (\n `${opening} ${servicePath}/model.likec4 already declares ${attested.length} call(s) across its boundary: ` +\n `${attested.map(spellCall).join(\", \")}. Carry each one up as ONE edge on \\`${id}\\`, collapsed to the ` +\n `service — ${forms} for a call, \\`publishes\\`/\\`consumes\\` ` +\n `for an event — naming the other party as the map already spells it; where the model's word names a ` +\n `different thing in the map, say so in the hand-back instead of drawing it. Do not add a second element: ` +\n `one already resolves. ${accountability(req, id)} Carry all of them up now, or say in the hand-back which ` +\n `you did not and why.${tags}`\n );\n }\n return (\n `${opening} Nothing in ${servicePath}/model.likec4 attests a call across its boundary — absent, unparseable ` +\n `and internal-only read alike here, because this brief may only name calls already attested. Draw the ones ` +\n `the walk's outbound and message stops (stops 4 and 7) find, each as one edge on \\`${id}\\` carrying ` +\n `\\`metadata { op '<operationId>' }\\` for a call or \\`publishes\\`/\\`consumes\\` for an event; if the service ` +\n `genuinely makes and receives no call, draw NOTHING — an invented edge is a dependency the fleet then plans ` +\n `against, and an edgeless element that is TRUE is a fact. Do not add a second element: one already resolves. ` +\n `Nothing in \\`loam validate --all\\` names this state while the model attests no call; this brief is the only ` +\n `place it is said.${tags}`\n );\n}\n"]}
1
+ {"version":3,"file":"owed.js","sourceRoot":"","sources":["../../../../src/core/brief/map/owed.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The write one service still owes `architecture/landscape.likec4`: the\n * target the brief hands over (`landscapeArtifact` — its shape rules and the\n * example block or edges) and the instruction in prose (`instructionFor` —\n * the file, the block, and the consequence, in the four states a landscape\n * can be in). Both exist to stop an agent inventing a parallel model: the\n * fleet has already drawn some of these boxes and edges, and a baseline has to\n * attach.\n *\n * A sub-package of `core/brief/` because that package sits at its five-file\n * cap and `landscape.ts` — which READS what the map already says about the\n * service and decides which state it is in — had parked at the file ceiling\n * with its object literals collapsed onto single lines to fit. The two halves\n * are distinct subjects: one is the derivation over a parsed document, the\n * other is all of the prose plus the vocabulary that prose reads (`BrokenMap`\n * and its builder live here for that reason — beside the arm that spends them,\n * not in the caller that fills them). The only tie back to the parent is\n * `import type { BriefTarget }`, which `verbatimModuleSyntax` erases, so this\n * package value-imports nothing from `../` and the package graph stays acyclic.\n */\nimport { brokenDocumentPaths, type LikeC4Error } from \"../../c4/likec4.js\";\nimport { spellCall, edgeTemplates, type AttestedCall } from \"../../c4/resolve/attested.js\";\nimport { serviceElementId } from \"../../c4/seed/template.js\";\nimport type { BriefTarget } from \"../targets.js\";\n\n/*\n * The identifier a brief suggests for an element the map does not draw yet is\n * `serviceElementId` — `loam seed`'s `svc_<id>`, never a convention of the\n * brief's own. It used to be the directory name camel-cased here, so on a\n * seeded fleet the brief handed an agent `paymentService` beside a map whose\n * every box was `svc_payment_service`. A suggestion and nothing more —\n * `metadata { service '<id>' }` is what joins the element to the directory —\n * but a brief handing over a concrete block gets followed, and one describing\n * a block in prose gets improvised on, so the spelling has to be the fleet's.\n * The `svc_` prefix also covers what the old helper special-cased by hand: a\n * service id may open with a digit, a LikeC4 identifier may not.\n */\n\n/* ------------------------------------------------------------------ */\n/* The target */\n/* ------------------------------------------------------------------ */\n\n/** What `landscapeArtifact` needs — one record, because the fields only ever travel together. */\nexport interface LandscapeArtifactRequest {\n service: string;\n expects: string[];\n present: boolean;\n servicePath: string;\n /** The element that already resolves to the service — set only in the edgeless state — and the calls its model attests. */\n elementId?: string;\n /**\n * Whether that element carries `#external`. It changes what the brief may\n * PROMISE and nothing else: `landscape.service-isolated` removes every\n * `#external` element from its subject set, so on such a tree no warning holds\n * the agent accountable, and saying one will is the false promise this flag\n * exists to stop (verification 2026-09-04).\n */\n external?: boolean;\n attested: AttestedCall[];\n}\n\n/**\n * The write this service still owes `architecture/landscape.likec4`.\n *\n * `expects` is what the fleet ALREADY calls on the service — those edges exist\n * in the map today, so the example draws the outbound side as a comment rather\n * than telling an agent to duplicate an edge that is already there. With\n * `elementId` set the example is EDGES ONLY, on the element that exists: a\n * block here would be the second box the edgeless state exists to refuse, and\n * the attested calls are listed as comments the agent turns into edges by\n * naming the other party as the map spells it.\n */\nfunction landscapeBlock(req: LandscapeArtifactRequest): string {\n const { service, expects, present } = req;\n if (req.elementId !== undefined) {\n const id = req.elementId;\n return [\n \"model {\",\n ` // ... every element stays exactly as it is — ${id} already resolves to ${req.servicePath}/ ...`,\n \"\",\n \" // every call in or out of it, one edge each, on the existing element:\",\n ` // ${id} -> <callee> 'Calls <operationId>' { metadata { op '<operationId>' } }`,\n ` // <caller> -> ${id} 'Calls <operationId>' { metadata { op '<operationId>' } }`,\n ...(req.attested.length > 0\n ? [\n ` // the calls ${req.servicePath}/model.likec4 already attests, as the model spells the other party:`,\n ...req.attested.map((c) => ` // ${id} ${spellCall(c)}`),\n ]\n : []),\n \"}\",\n ].join(\"\\n\");\n }\n const id = serviceElementId(service);\n const calls =\n expects.length > 0\n ? expects.map((op) => `// <caller> -> ${id} 'Calls ${op}' { metadata { op '${op}' } }`)\n : [`// <caller> -> ${id} 'Calls <operationId>' { metadata { op '<operationId>' } }`];\n const model = [\n \"model {\",\n ...(present ? [\" // ... the fleet's other services stay exactly as they are ...\", \"\"] : []),\n ` ${id} = softwareSystem '${service}' {`,\n \" description '<one line: what this service is responsible for>'\",\n ` metadata { service '${service}' }`,\n \" }\",\n \"\",\n \" // every call in or out of it, one edge each:\",\n ...calls.map((line) => ` ${line}`),\n \"}\",\n ];\n // When the file does not exist the example is the WHOLE file, so it carries\n // the `specification` block a bare `model` would be rejected without: the\n // brief must never hand over a document `loam validate` refuses. When the file\n // does exist the example is a fragment to splice, and repeating a\n // `specification` block there would invite a second one.\n return present\n ? model.join(\"\\n\")\n : [\"specification {\", \" element softwareSystem\", \"}\", \"\", ...model].join(\"\\n\");\n}\n\nconst OP_EDGE_RULE =\n \"Draw every cross-service call as an edge carrying the operation it uses: `a -> b 'Calls createSplit' { metadata { op 'createSplit' } }`. The `op` must be an operationId the TARGET's openapi.yaml defines, or `spine.op-undefined` (error) — a broken contract between services.\";\n\n/** What the edgeless state costs when a check is watching — the ordinary case. */\nconst ISOLATED_RULE = (servicePath: string): string =>\n `\\`loam validate --all\\` reports \\`landscape.service-isolated\\` (warn) while this element has no edge and ` +\n `\\`${servicePath}/model.likec4\\` declares a call across its boundary; with no such call it stays silent, so ` +\n `an edgeless service that truly calls nothing is correct, and this brief says so.`;\n\n/**\n * The same state under `#external`, where NO check is watching.\n *\n * `landscape.service-isolated` removes every `#external` element from its\n * subject set — the fix that stopped a foreign box answering for one of our\n * directories — so on this tree the warning the brief used to promise cannot\n * fire, measured: `validate --all` reports `landscape.matched` and the full\n * `--json` findings walk holds no isolation finding at all. A brief that names\n * a warning nothing will raise is worse than one that says nothing, because the\n * agent budgets against the check instead of against the sentence.\n */\nconst EXTERNAL_RULE = (elementId: string, servicePath: string): string =>\n `\\`${elementId}\\` carries \\`#external\\`, so \\`landscape.service-isolated\\` will NOT fire here whatever this ` +\n `element's edges are — the check skips every element the map marks as somebody else's, exactly as the two ` +\n `binding checks do. Nothing in \\`loam validate --all\\` names this state; this brief is the only place it is ` +\n `said. If \\`${servicePath}/\\` is ours, drop \\`#external\\` from the element and the check applies again; if it ` +\n `is genuinely somebody else's, the calls belong on the element of the service that makes them.`;\n\n/** The fleet map's own brief — assembled per service, because the write it owes names the service. */\nexport function landscapeArtifact(req: LandscapeArtifactRequest): Omit<BriefTarget, \"path\" | \"exists\" | \"action\"> {\n const { service, servicePath, elementId } = req;\n const whole =\n \"This file is the WHOLE FLEET's, not this service's. Add to it; never rewrite it. Everything already in `model { ... }` belongs to the other services, and replacing the file destroys their map along with their edges.\";\n return {\n artifact: \"landscape.likec4\",\n required: true,\n purpose: `the fleet map — ${\n elementId === undefined\n ? \"until an element here resolves to this directory, the service is documented\"\n : \"an element here resolves to this directory and no edge touches it, so the service is drawn\"\n } and invisible to every cross-service check`,\n shape:\n elementId === undefined\n ? [\n whole,\n `Add one top-level element for the service, bound to its directory: \\`metadata { service '${service}' }\\`. Until an element resolves to \\`${servicePath}/\\`, \\`loam validate --all\\` reports \\`landscape.service-unmodelled\\` (error).`,\n \"Bind rather than rename: a binding whose id names no directory is `landscape.binding-unknown` (error), and two elements binding the SAME directory is `landscape.binding-duplicate` (warn) — every element→service join then picks one of them arbitrarily.\",\n OP_EDGE_RULE,\n \"If the file does not exist yet, create it with a `specification { ... }` block declaring the kinds you use, then the `model { ... }` block. A landscape that does not parse is `landscape.invalid` (error) and blinds every cross-service check at once.\",\n ]\n : [\n whole,\n `An element already resolves to \\`${servicePath}/\\` (\\`${elementId}\\`). Do NOT add a second one — two elements resolving to one directory is \\`landscape.binding-duplicate\\` (warn) and every element→service join then picks one of them arbitrarily. What the map lacks is every call in or out of it.`,\n OP_EDGE_RULE,\n req.external === true ? EXTERNAL_RULE(elementId, servicePath) : ISOLATED_RULE(servicePath),\n ],\n example: landscapeBlock(req),\n };\n}\n\n/* ------------------------------------------------------------------ */\n/* The instruction */\n/* ------------------------------------------------------------------ */\n\n/** Which documents of `architecture/` failed, and how many errors they carry between them. */\nexport interface BrokenMap {\n /** Docs-relative POSIX paths, deduped and sorted — spelled as `landscape.invalid` spells them. */\n paths: string[];\n errors: number;\n}\n\n/**\n * That record, built from a project's diagnostics — beside the type it fills and\n * the arm that reads it, rather than in the caller.\n *\n * Spelled exactly as `commands/validate/fleet/landscape.ts` spells it for\n * `landscape.invalid` — deduped, relative to the docs root, `/`-joined — because\n * the two surfaces are read one after the other on the same tree and an agent\n * has to be able to tell that they name the same file: both go through\n * `c4/likec4.ts`'s `brokenDocumentPaths`.\n */\nexport function brokenDocuments(docsDir: string, land: { errors: readonly LikeC4Error[] }): BrokenMap {\n return { paths: brokenDocumentPaths(docsDir, land.errors), errors: land.errors.length };\n}\n\n/** The inputs of `instructionFor` — a record, because the function sat at the parameter cap. */\nexport interface InstructionRequest {\n service: string;\n state: \"absent\" | \"unparseable\" | \"unmodelled\" | \"edgeless\";\n expects: string[];\n servicePath: string;\n /**\n * The documents that failed (`unparseable` state only). Optional so the other\n * three arms need not carry it, and the arm degrades to naming the project\n * rather than guessing a file when a caller has none.\n */\n broken?: BrokenMap;\n /** The element that already resolves to the service (edgeless state only), and its tags loam does not read. */\n elementId?: string;\n /** Whether that element carries `#external` — see `LandscapeArtifactRequest.external`. */\n external?: boolean;\n attested: AttestedCall[];\n foreignTags: string[];\n}\n\n/**\n * The fleet-map instruction, in the four states a landscape can be in. It says\n * the file, the block, and the consequence, in that order. The consequence is\n * the part agents acted on: \"add an element\" reads as tidiness, \"until you do,\n * `loam validate --all` fails and no cross-service check can see this service\"\n * reads as work.\n */\nexport function instructionFor(req: InstructionRequest): string {\n const { service, state, expects, servicePath } = req;\n const id = serviceElementId(service);\n const bind = `an element bound with \\`metadata { service '${service}' }\\``;\n const consequence =\n `Until one resolves, \\`loam validate --all\\` reports \\`landscape.service-unmodelled\\` (error) for ` +\n `${servicePath}/: the service is documented and invisible — no edge into it can be checked ` +\n `against its openapi.yaml, and no feature can draw a call to it.`;\n // `expects` is reachable only through inbound edges, so it is empty in every\n // state that ASKS for an element — the edgeless one by definition. (The\n // unparseable state can carry it, off the landscape-file fallback, and that\n // arm deliberately does not append this sentence: it asks for no element, so\n // \"your element is what they will resolve to\" would name one loam has not\n // read.) It is threaded through anyway rather than dropped: the caller\n // computes it once, and the day an unmodelled service can inherit an edge (a\n // container-level binding, say) the sentence is already here.\n const owed =\n expects.length > 0\n ? ` The fleet already calls ${expects.map((o) => `'${o}'`).join(\", \")} on this service, so those edges exist; your element is what they will resolve to.`\n : \"\";\n\n if (state === \"absent\") {\n return (\n `architecture/landscape.likec4 does not exist yet. Create it with a \\`specification { ... }\\` block ` +\n `declaring the kinds you use and a \\`model { ... }\\` block containing ${bind} ` +\n `(conventionally \\`${id} = softwareSystem '${service}'\\`), plus one edge per cross-service call, each ` +\n `carrying \\`metadata { op '<operationId>' }\\`. ${consequence}${owed}`\n );\n }\n if (state === \"unparseable\") {\n // The map is the whole `architecture/` project, so the document that broke\n // is usually a SIBLING of the landscape — a use case, a palette. This arm\n // named `architecture/landscape.likec4` regardless and told an agent to go\n // and fix parse errors in a file that has none, contradicting the\n // `landscape.invalid` that `validate --all` prints on the same tree one\n // second later. The EDGELESS arm below carries the same rule in its own WHY:\n // never name bytes loam did not read.\n const b = req.broken;\n const named =\n b === undefined || b.paths.length === 0\n ? \"The fleet map (architecture/) does not parse\"\n : `${b.paths.join(\", \")} ${b.paths.length === 1 ? \"does\" : \"do\"} not parse (${String(b.errors)} error(s)` +\n `${b.paths.length === 1 ? \"\" : \" between them\"}), so the fleet map (architecture/) cannot be read`;\n return (\n `${named} (\\`landscape.invalid\\`). Nothing can be said about what the map already models — not that it ` +\n `lacks an element for ${servicePath}/, and not that it holds one — so this brief asks for no edit to it. ` +\n `Fix the parse errors first (they blind every cross-service check at once, and \\`loam validate --all\\` ` +\n `names the same file), then re-run \\`loam adopt --service ${service}\\`: it will say whether the map already ` +\n `holds ${bind}, and adding one before then risks a second box for a service the map already draws ` +\n `(\\`landscape.binding-duplicate\\`).`\n );\n }\n if (state === \"edgeless\") return edgelessInstruction(req);\n return (\n `Nothing in architecture/landscape.likec4 resolves to ${servicePath}/. ADD to that file — do not rewrite ` +\n `it, every other service's map is in there — ${bind}, conventionally ` +\n `\\`${id} = softwareSystem '${service}'\\`, inside the existing \\`model { ... }\\` block, and draw each ` +\n `cross-service call as an edge carrying \\`metadata { op '<operationId>' }\\`. ${consequence}${owed}`\n );\n}\n\n/**\n * The fourth state, in two arms: with attested calls it names each and says to\n * carry it up as ONE edge on the existing element; without, it points at the\n * walk stops that produce the evidence and otherwise says draw NOTHING — an\n * invented edge is a dependency the fleet then plans against. Neither arm asks\n * for a second element, and both name the tags loam does not read.\n */\n/**\n * Which sentence the attested arm may end on: what will hold the agent to this\n * edit, or the admission that nothing will.\n *\n * `landscape.service-isolated` skips every `#external` element, so on such a\n * tree the promise \"reports it until an edge lands\" is one the check cannot\n * keep — and the brief was making it on exactly the tree that fix was written\n * for (verification 2026-09-04). The `#external` arm says so instead, and says\n * how to make the check apply.\n */\nfunction accountability(req: InstructionRequest, id: string): string {\n if (req.external === true) {\n return (\n `\\`${id}\\` carries \\`#external\\`, so \\`landscape.service-isolated\\` will NOT fire here whatever you draw — ` +\n `the check skips every element the map marks as somebody else's. Nothing in \\`loam validate --all\\` names ` +\n `this state; this brief is the only place it is said. Drop \\`#external\\` from the element if ` +\n `${req.servicePath}/ is ours, and the check applies again.`\n );\n }\n return (\n \"`loam validate --all` reports `landscape.service-isolated` (warn) until an edge lands — ONE edge, on any \" +\n \"of them: the check is touched/untouched, not a set difference, so nothing will name the calls you leave \" +\n \"undrawn.\"\n );\n}\n\nfunction edgelessInstruction(req: InstructionRequest): string {\n const { servicePath, attested, foreignTags } = req;\n const id = req.elementId ?? serviceElementId(req.service);\n // \"the fleet map (architecture/)\" rather than the landscape FILE: the map is\n // the whole project, an element may be declared in any document of it, and\n // LikeC4 hands back no source document for an element — so naming a file\n // here would be a claim about bytes loam did not read. The write still lands\n // in `architecture/landscape.likec4`; the target's `path` says so.\n const opening =\n `\\`${id}\\` in the fleet map (architecture/) resolves to ${servicePath}/ and no edge in the map touches it — ` +\n `the service is drawn and invisible to every cross-service check.`;\n const tags =\n foreignTags.length === 0\n ? \"\"\n : ` The element carries ${foreignTags.map((t) => `#${t}`).join(\", \")} — tags loam does not read; if one marks a ` +\n `placeholder drawn ahead of adoption, clearing it is part of this edit, because a view written ` +\n `\\`exclude element.tag = #<that>\\` keeps an adopted service hidden.`;\n if (attested.length > 0) {\n // The edge form follows the calls' direction (`edgeTemplates`): this arm\n // used to spell the outbound form alone, so an inbound call was carried\n // up backwards.\n const forms = edgeTemplates(id, attested)\n .map((t) => `\\`${t}\\``)\n .join(\" or \");\n return (\n `${opening} ${servicePath}/model.likec4 already declares ${attested.length} call(s) across its boundary: ` +\n `${attested.map(spellCall).join(\", \")}. Carry each one up as ONE edge on \\`${id}\\`, collapsed to the ` +\n `service — ${forms} for a call, \\`publishes\\`/\\`consumes\\` ` +\n `for an event — naming the other party as the map already spells it; where the model's word names a ` +\n `different thing in the map, say so in the hand-back instead of drawing it. Do not add a second element: ` +\n `one already resolves. ${accountability(req, id)} Carry all of them up now, or say in the hand-back which ` +\n `you did not and why.${tags}`\n );\n }\n return (\n `${opening} Nothing in ${servicePath}/model.likec4 attests a call across its boundary — absent, unparseable ` +\n `and internal-only read alike here, because this brief may only name calls already attested. Draw the ones ` +\n `the walk's outbound and message stops (stops 4 and 7) find, each as one edge on \\`${id}\\` carrying ` +\n `\\`metadata { op '<operationId>' }\\` for a call or \\`publishes\\`/\\`consumes\\` for an event; if the service ` +\n `genuinely makes and receives no call, draw NOTHING — an invented edge is a dependency the fleet then plans ` +\n `against, and an edgeless element that is TRUE is a fact. Do not add a second element: one already resolves. ` +\n `Nothing in \\`loam validate --all\\` names this state while the model attests no call; this brief is the only ` +\n `place it is said.${tags}`\n );\n}\n"]}
@@ -7,6 +7,21 @@
7
7
  * it against a docs repo, and the fleet map's own target, whose block names
8
8
  * the service, is assembled per service in map/owed.ts.
9
9
  */
10
+ /**
11
+ * The service-local use-case directory, and the one artifact key `ServicePaths`
12
+ * deliberately does NOT carry.
13
+ *
14
+ * `repo/paths.ts` spells the paths loam JOINS ON — the ones a check resolves, a
15
+ * writer writes and the tree walk classifies a directory by. This is not one of
16
+ * them: the slot a service's flows are graded out of is the service's whole
17
+ * LikeC4 project, every `.likec4` under its directory
18
+ * (`core/usecases/service/flows.ts`), and `usecases/<name>.likec4` is the
19
+ * CONVENTION the brief publishes for it. Putting the directory in
20
+ * `ARTIFACT_FILES` would be worse than redundant — `isServiceArtifactName`
21
+ * reads that table, so a `usecases/` directory anywhere under `services/` would
22
+ * mint a service.
23
+ */
24
+ export const USECASES_DIR = "usecases";
10
25
  /** The artifacts, in the order a baseline is best written in. */
11
26
  export const ARTIFACTS = [
12
27
  {
@@ -78,19 +93,26 @@ views {
78
93
  "`Operations: <operationId>[, <operationId>]` in the requirement body names the API operations it governs. Every one must exist in this service's openapi.yaml — a name that does not is `spec-api.op-undefined` (error), reported against the LIVING spec, not only inside a feature delta.",
79
94
  "Document what the service DOES today, not what it should do. This is the baseline; changes to it belong in a feature delta.",
80
95
  ],
96
+ // `<svc>` is the model example's placeholder, filled the same way: the
97
+ // frontmatter `service:` must equal the directory it sits in, so a header
98
+ // naming payment-service was a paste that failed `frontmatter.field-mismatch`
99
+ // for every other service. And the blank line under each `### Requirement:`
100
+ // is the canonical shape `serializeRequirements` writes — a living spec
101
+ // copied from an example without it is reflowed by its first archive.
81
102
  example: `---
82
- service: payment-service
103
+ service: <svc>
83
104
  status: draft
84
105
  owner: payments-team
85
106
  sources:
86
107
  - src/main/java/com/shop/payment/
87
108
  ---
88
109
 
89
- # payment-service
110
+ # <svc>
90
111
 
91
112
  ## Requirements
92
113
 
93
114
  ### Requirement: Authorize a payment
115
+
94
116
  WHEN authorization is requested for a captured-eligible order THE SYSTEM SHALL
95
117
  authorize it against the issuer before any capture.
96
118
 
@@ -102,6 +124,7 @@ Operations: authorizePayment
102
124
  - **Then** the payment is authorized and an authorization id is returned
103
125
 
104
126
  ### Requirement: Refunding is permission-gated
127
+
105
128
  IF the caller lacks the refund permission THEN THE SYSTEM SHALL refuse the
106
129
  request and leave the payment untouched.
107
130
 
@@ -131,19 +154,21 @@ Operations: refundPayment
131
154
  "Optional, but expected for a service with real architecture: every alert/SLI health.yaml declares wants a covering requirement here (`health.uncovered`, warn) — a signal nothing tests is dashboard decoration.",
132
155
  "Write the obligations no business requirement states — the transactional outbox, what a caller's retry may assume, what pages whom. An arch scenario becomes an integration/ops test, not an acceptance test.",
133
156
  ],
157
+ // Same placeholder and same canonical requirement shape as spec.md's above.
134
158
  example: `---
135
- service: payment-service
159
+ service: <svc>
136
160
  status: draft
137
161
  owner: payments-team
138
162
  sources:
139
163
  - src/main/java/com/shop/payment/
140
164
  ---
141
165
 
142
- # payment-service — architecture
166
+ # <svc> — architecture
143
167
 
144
168
  ## Requirements
145
169
 
146
170
  ### Requirement: Events leave through the transactional outbox
171
+
147
172
  The service SHALL write a domain event and its state change in one transaction,
148
173
  published by an outbox relay — never a dual write.
149
174
 
@@ -153,6 +178,55 @@ Covers: paymentService.db, paymentService -> kafka
153
178
  - **Given** an event in the outbox
154
179
  - **When** the broker is unavailable
155
180
  - **Then** the state stays committed and the event is published once it returns
181
+ `,
182
+ },
183
+ {
184
+ artifact: "usecases/<name>.likec4",
185
+ key: "usecasesDir",
186
+ // Never required, and here the optionality is a RULE rather than a
187
+ // judgement. docs/DESIGN.md rule 26 — "presence is never owed. A model with
188
+ // no views is missing nothing loam wants, and no check may grade its
189
+ // absence" — is enforced by scripts/arch-check.mjs, not merely written, so
190
+ // there is no `service.usecases-missing` and there never will be one.
191
+ //
192
+ // The slot is briefed anyway because a team cannot choose a slot nobody
193
+ // named. The grader landed 2026-09-03 (`core/usecases/service/flows.ts`) and
194
+ // `c4.valid` has been reporting ` · K tagged flow(s) graded` ever since, on
195
+ // an axis nothing scaffolded, briefed or named — so the only way to discover
196
+ // it was to read the source. Publication is the whole of this entry: every
197
+ // rule below is either a check that already exists or a convention loam
198
+ // states and never grades, and each says which it is.
199
+ required: false,
200
+ purpose: "optional — one requirement's hop sequence, drawn as a LikeC4 dynamic view over this service's own containers",
201
+ shape: [
202
+ "A `dynamic view` in a `.likec4` under this service's directory. THE SLOT IS THE PROJECT: `loam subsystem sync` registers every `.likec4` under `services/<…>/<id>/` as one LikeC4 project, and loam reads exactly the set the renderer reads — `model.likec4` plus every sibling, recursively. `usecases/<name>.likec4` is the convention this brief publishes, and `<name>` is yours. Never `architecture/usecases/`, which is a different project: it holds the map and no service's containers, so a hop naming one is `landscape.invalid` for the whole fleet.",
203
+ "`#req-<Requirement-ID>` as the FIRST thing in the view body, before `title` — written after it, LikeC4 reports five parse errors and none of them mentions tags. The tag is the opt-in: an untagged view is somebody's hand-drawn diagram and loam never says a word about it. The slug is the `Requirement-ID` from this service's own `spec.md` or `arch.spec.md` with every character outside `[A-Za-z0-9_-]` flattened to `-`; a slug naming no requirement of this service is `usecase.requirement-unresolved`. Several `#req-` tags on one view are legal and normal.",
204
+ "DECLARE the tag exactly once — in `architecture/landscape.likec4`'s `specification` block when this model EXTENDS the map (the usual shape, and the block the whole root project shares), or in `model.likec4`'s own `specification` when the model STANDS ALONE. Declaring it in both is a duplicate, and the code lands where the SECOND declaration's file is: `c4.invalid` when a tags-only block sits in model.likec4, `usecase.flow-invalid` when it sits in a sibling. Two SERVICES each declaring the same tag locally is the fleet-scope class one step out, `c4.fleet-project-invalid`.",
205
+ "Every hop must be backed by a relationship the model already declares, or it is `usecase.step-unbacked` — LikeC4 itself reports nothing for an unbacked hop, so the diagram renders and every other check stays green. A return step is written `<target> <- <source>`: a reply is attributed to the CALL it answers. A hop whose caller and provider resolve to the SAME service never owes an `operationId`, which is what makes this altitude cheap: `usecase.step-unlinked` is silent inside one service.",
206
+ "Namespace the view id by the service — `uc_payment_service_authorize`, not `uc_authorize`. An EXTENDING model's views live in the ROOT project beside the map, so view ids are one flat namespace across the whole fleet: two services minting `uc_authorize` makes the renderer refuse the entire project. Ids beginning `subsystem_` or `service_` are loam's own (`subsystem.view-id-collision`).",
207
+ "Title it `'<subsystem path> / <service id> / <use case>'`. LikeC4 has no view-folder syntax — it splits a view title on `/` into browser folders and shows only the last segment (measured at 1.59.2) — so that convention lands the flow in the same folder as the generated overview of its service in `architecture/services.likec4`. **Nothing checks this**: it is a convention loam states and never grades, exactly like the EARS form in spec.md.",
208
+ "OPTIONAL, and its absence is graded by nothing, ever. That is docs/DESIGN.md rule 26 and it is enforced. Write one where a requirement's `Covers:` names containers and the sequence is the thing that is hard to hold in the head; write none otherwise. `c4.valid` reports ` · K tagged flow(s) graded` when the project declares any, and that count is the only place a healthy flow appears at all.",
209
+ ],
210
+ example: `// One of <svc>'s own use cases: the hops behind ONE requirement of its
211
+ // arch.spec.md, over its OWN containers. Optional — nothing grades the
212
+ // absence of this file, and an untagged view here is never graded either.
213
+ views {
214
+ // Namespace the id by the service, underscores for hyphens: an extending
215
+ // model's views share ONE flat id namespace with the whole root project.
216
+ dynamic view uc_authorize_payment {
217
+ // The tag comes FIRST. Written after 'title' this is five parse errors.
218
+ // AUTH-1 is a Requirement-ID in this service's spec.md or arch.spec.md.
219
+ #req-AUTH-1
220
+ title '<subsystem path> / <svc> / Authorize a payment'
221
+ description 'What the service does between the request and the answer'
222
+
223
+ // Every hop is backed by a relationship model.likec4 declares. Both of
224
+ // these are inside one service, so neither owes an operationId.
225
+ <fqn>.api -> <fqn>.ledger 'Records the authorization'
226
+ // A reply is attributed to the call it answers, hence the '<-'.
227
+ <fqn>.api <- <fqn>.ledger 'Confirms the write'
228
+ }
229
+ }
156
230
  `,
157
231
  },
158
232
  {
@@ -186,7 +260,7 @@ Covers: paymentService.db, paymentService -> kafka
186
260
  required: false,
187
261
  purpose: "the async contract — the messages this service puts on and takes off the bus",
188
262
  shape: [
189
- "AsyncAPI **3.0** with `channels`, `operations` and `components.messages`. Only 3.0 is read: its operations are named top-level objects carrying `action: send|receive`, which is what makes a message's direction legible. A 2.x document declares no `operations` and reads as a contract with no messages.",
263
+ "AsyncAPI **3.0** with `channels`, `operations` and `components.messages`. Only 3.0 is read: its operations are named top-level objects carrying `action: send|receive`, which is what makes a message's direction legible. A 1.x or 2.x document is refused as `asyncapi.invalid` and nothing on the event axis is graded against it; upgrade an existing one with `asyncapi convert --target-version=3.0.0` rather than copying it in as is.",
190
264
  "EVERY message carries a `name` (or is declared under the key you intend to reference). That token is the spine: the C4 edge's `metadata { publishes }` / `metadata { consumes }`, the requirement's `Publishes:` / `Consumes:` line and this name are one name spelled three times.",
191
265
  "Namespace the name by the domain that owns it — `payment.PaymentAuthorized`. One message has exactly one producer; two services declaring they send one name is `asyncapi.message-contested`, and then which contract a consumer reads is a coin flip.",
192
266
  "loam reads message names and the `action` of the operation carrying them. **It never joins on anything inside `payload`** — the one look inside is a presence probe: a payload declaring no shape at all warns (`asyncapi.payload-undescribed`), and a payload with a non-JSON `schemaFormat` is skipped outright. Write the payload as JSON Schema today; if the fleet later adopts Avro, that is a `schemaFormat` line here and nothing in loam changes.",