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

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 (270) hide show
  1. package/CHANGELOG.md +947 -0
  2. package/MIGRATING-from-OpenSpec.md +7 -1
  3. package/QUICKSTART.md +8 -3
  4. package/README.md +20 -13
  5. package/ROADMAP.md +329 -22
  6. package/SCHEMA.md +472 -24
  7. package/WORKFLOW.md +23 -10
  8. package/dist/commands/adopt/render.js +47 -8
  9. package/dist/commands/adopt/render.js.map +1 -1
  10. package/dist/commands/archive/plan/landscape.js +45 -1
  11. package/dist/commands/archive/plan/landscape.js.map +1 -1
  12. package/dist/commands/archive/plan/model/extending.js +85 -0
  13. package/dist/commands/archive/plan/model/extending.js.map +1 -0
  14. package/dist/commands/archive/run.js +5 -2
  15. package/dist/commands/archive/run.js.map +1 -1
  16. package/dist/commands/context/print.js +11 -1
  17. package/dist/commands/context/print.js.map +1 -1
  18. package/dist/commands/doctor.js +46 -0
  19. package/dist/commands/doctor.js.map +1 -1
  20. package/dist/commands/explore.js +12 -2
  21. package/dist/commands/explore.js.map +1 -1
  22. package/dist/commands/list/views.js +5 -1
  23. package/dist/commands/list/views.js.map +1 -1
  24. package/dist/commands/seed/plan.js +97 -3
  25. package/dist/commands/seed/plan.js.map +1 -1
  26. package/dist/commands/seed/seed.js +7 -2
  27. package/dist/commands/seed/seed.js.map +1 -1
  28. package/dist/commands/show/feature.js +2 -1
  29. package/dist/commands/show/feature.js.map +1 -1
  30. package/dist/commands/show/marks.js +3 -5
  31. package/dist/commands/show/marks.js.map +1 -1
  32. package/dist/commands/show/service.js +39 -18
  33. package/dist/commands/show/service.js.map +1 -1
  34. package/dist/commands/subsystem/sync.js +184 -39
  35. package/dist/commands/subsystem/sync.js.map +1 -1
  36. package/dist/commands/subsystem/txn/exclude.js +132 -0
  37. package/dist/commands/subsystem/txn/exclude.js.map +1 -0
  38. package/dist/commands/subsystem/txn/report.js +124 -0
  39. package/dist/commands/subsystem/txn/report.js.map +1 -0
  40. package/dist/commands/subsystem/txn/txn.js +29 -3
  41. package/dist/commands/subsystem/txn/txn.js.map +1 -1
  42. package/dist/commands/subsystem/txn/views.js +113 -16
  43. package/dist/commands/subsystem/txn/views.js.map +1 -1
  44. package/dist/commands/validate/arch-coverage.js +16 -9
  45. package/dist/commands/validate/arch-coverage.js.map +1 -1
  46. package/dist/commands/validate/checks/fleet-shape.js +30 -77
  47. package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
  48. package/dist/commands/validate/checks/vocabulary.js +4 -19
  49. package/dist/commands/validate/checks/vocabulary.js.map +1 -1
  50. package/dist/commands/validate/feature.js +1 -1
  51. package/dist/commands/validate/feature.js.map +1 -1
  52. package/dist/commands/validate/fleet/census.js +11 -10
  53. package/dist/commands/validate/fleet/census.js.map +1 -1
  54. package/dist/commands/validate/fleet/landscape.js +128 -114
  55. package/dist/commands/validate/fleet/landscape.js.map +1 -1
  56. package/dist/commands/validate/fleet/load.js +66 -0
  57. package/dist/commands/validate/fleet/load.js.map +1 -1
  58. package/dist/commands/validate/fleet/map/attest.js +103 -0
  59. package/dist/commands/validate/fleet/map/attest.js.map +1 -0
  60. package/dist/commands/validate/fleet/map/bindings.js +96 -0
  61. package/dist/commands/validate/fleet/map/bindings.js.map +1 -0
  62. package/dist/commands/validate/fleet/map/consumers.js +298 -0
  63. package/dist/commands/validate/fleet/map/consumers.js.map +1 -0
  64. package/dist/commands/validate/fleet/map/isolation.js +116 -0
  65. package/dist/commands/validate/fleet/map/isolation.js.map +1 -0
  66. package/dist/commands/validate/fleet/map/usecases.js +56 -0
  67. package/dist/commands/validate/fleet/map/usecases.js.map +1 -0
  68. package/dist/commands/validate/fleet/usecases/steps.js +49 -17
  69. package/dist/commands/validate/fleet/usecases/steps.js.map +1 -1
  70. package/dist/commands/validate/fleet/views/fleet-project.js +217 -0
  71. package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -0
  72. package/dist/commands/validate/fleet/views/ids.js +13 -9
  73. package/dist/commands/validate/fleet/views/ids.js.map +1 -1
  74. package/dist/commands/validate/fleet/views/projects.js +278 -0
  75. package/dist/commands/validate/fleet/views/projects.js.map +1 -0
  76. package/dist/commands/validate/fleet/views/root-documents.js +97 -0
  77. package/dist/commands/validate/fleet/views/root-documents.js.map +1 -0
  78. package/dist/commands/validate/fleet/views/stale.js +2 -2
  79. package/dist/commands/validate/fleet/views/stale.js.map +1 -1
  80. package/dist/commands/validate/service/model/diverged.js +226 -0
  81. package/dist/commands/validate/service/model/diverged.js.map +1 -0
  82. package/dist/commands/validate/service/model/grade.js +163 -0
  83. package/dist/commands/validate/service/model/grade.js.map +1 -0
  84. package/dist/commands/validate/service/service.js +110 -34
  85. package/dist/commands/validate/service/service.js.map +1 -1
  86. package/dist/commands/validate/service/specs.js +39 -5
  87. package/dist/commands/validate/service/specs.js.map +1 -1
  88. package/dist/commands/validate/service/spine.js +41 -3
  89. package/dist/commands/validate/service/spine.js.map +1 -1
  90. package/dist/commands/validate/service/usecases/tags.js +113 -0
  91. package/dist/commands/validate/service/usecases/tags.js.map +1 -0
  92. package/dist/commands/validate/service/usecases/usecases.js +209 -0
  93. package/dist/commands/validate/service/usecases/usecases.js.map +1 -0
  94. package/dist/commands/validate/validate.js +10 -24
  95. package/dist/commands/validate/validate.js.map +1 -1
  96. package/dist/core/agent/agents-md/artifacts.js +1 -0
  97. package/dist/core/agent/agents-md/artifacts.js.map +1 -1
  98. package/dist/core/agent/agents-md/command-map.js +18 -2
  99. package/dist/core/agent/agents-md/command-map.js.map +1 -1
  100. package/dist/core/agent/agents-md/cycle.js +24 -9
  101. package/dist/core/agent/agents-md/cycle.js.map +1 -1
  102. package/dist/core/agent/agents-md/map/lenses/context.js +4 -3
  103. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
  104. package/dist/core/agent/agents-md/map/subsystem.js +51 -2
  105. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
  106. package/dist/core/agent/agents-md/map/usecases.js +45 -3
  107. package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
  108. package/dist/core/agent/agents-md.js +14 -4
  109. package/dist/core/agent/agents-md.js.map +1 -1
  110. package/dist/core/agent/support/report.js +39 -5
  111. package/dist/core/agent/support/report.js.map +1 -1
  112. package/dist/core/agent/workflows/adopt.js +125 -6
  113. package/dist/core/agent/workflows/adopt.js.map +1 -1
  114. package/dist/core/agent/workflows/check.js +35 -19
  115. package/dist/core/agent/workflows/check.js.map +1 -1
  116. package/dist/core/agent/workflows/closing.js +3 -1
  117. package/dist/core/agent/workflows/closing.js.map +1 -1
  118. package/dist/core/brief/brief.js +63 -11
  119. package/dist/core/brief/brief.js.map +1 -1
  120. package/dist/core/brief/checks.js +138 -3
  121. package/dist/core/brief/checks.js.map +1 -1
  122. package/dist/core/brief/landscape.js +212 -129
  123. package/dist/core/brief/landscape.js.map +1 -1
  124. package/dist/core/brief/map/extend.js +81 -0
  125. package/dist/core/brief/map/extend.js.map +1 -0
  126. package/dist/core/brief/map/owed.js +284 -0
  127. package/dist/core/brief/map/owed.js.map +1 -0
  128. package/dist/core/brief/targets.js +30 -26
  129. package/dist/core/brief/targets.js.map +1 -1
  130. package/dist/core/brief/unchecked.js +12 -2
  131. package/dist/core/brief/unchecked.js.map +1 -1
  132. package/dist/core/c4/likec4.js +23 -1
  133. package/dist/core/c4/likec4.js.map +1 -1
  134. package/dist/core/c4/parsed/deployment.js +5 -3
  135. package/dist/core/c4/parsed/deployment.js.map +1 -1
  136. package/dist/core/c4/parsed/view-ids.js +39 -1
  137. package/dist/core/c4/parsed/view-ids.js.map +1 -1
  138. package/dist/core/c4/prefetch/fleet.js +142 -0
  139. package/dist/core/c4/prefetch/fleet.js.map +1 -0
  140. package/dist/core/c4/project/architecture.js +52 -4
  141. package/dist/core/c4/project/architecture.js.map +1 -1
  142. package/dist/core/c4/project/batch.js +140 -0
  143. package/dist/core/c4/project/batch.js.map +1 -0
  144. package/dist/core/c4/project/documents.js +9 -4
  145. package/dist/core/c4/project/documents.js.map +1 -1
  146. package/dist/core/c4/project/load.js +77 -38
  147. package/dist/core/c4/project/load.js.map +1 -1
  148. package/dist/core/c4/project/staged.js +46 -12
  149. package/dist/core/c4/project/staged.js.map +1 -1
  150. package/dist/core/c4/resolve/attested.js +0 -0
  151. package/dist/core/c4/resolve/attested.js.map +1 -0
  152. package/dist/core/c4/resolve/service.js +8 -17
  153. package/dist/core/c4/resolve/service.js.map +1 -1
  154. package/dist/core/c4/resolve/steps.js +18 -0
  155. package/dist/core/c4/resolve/steps.js.map +1 -1
  156. package/dist/core/c4/root-project/exclude.js +352 -0
  157. package/dist/core/c4/root-project/exclude.js.map +1 -0
  158. package/dist/core/c4/service-model/documents.js +47 -0
  159. package/dist/core/c4/service-model/documents.js.map +1 -0
  160. package/dist/core/c4/service-model/fleet/extending.js +82 -0
  161. package/dist/core/c4/service-model/fleet/extending.js.map +1 -0
  162. package/dist/core/c4/service-model/load.js +165 -0
  163. package/dist/core/c4/service-model/load.js.map +1 -0
  164. package/dist/core/c4/service-model/renderer.js +128 -0
  165. package/dist/core/c4/service-model/renderer.js.map +1 -0
  166. package/dist/core/c4/service-model/shape.js +126 -0
  167. package/dist/core/c4/service-model/shape.js.map +1 -0
  168. package/dist/core/c4/service-model/slice.js +188 -0
  169. package/dist/core/c4/service-model/slice.js.map +1 -0
  170. package/dist/core/c4/source-scan.js +26 -0
  171. package/dist/core/c4/source-scan.js.map +1 -1
  172. package/dist/core/c4/splice/authored-source.js +60 -13
  173. package/dist/core/c4/splice/authored-source.js.map +1 -1
  174. package/dist/core/c4/splice/contract.js.map +1 -1
  175. package/dist/core/c4/splice/identity/declared.js +87 -0
  176. package/dist/core/c4/splice/identity/declared.js.map +1 -0
  177. package/dist/core/c4/splice/identity/edges.js +17 -0
  178. package/dist/core/c4/splice/identity/edges.js.map +1 -1
  179. package/dist/core/c4/splice/identity/existing.js +80 -0
  180. package/dist/core/c4/splice/identity/existing.js.map +1 -0
  181. package/dist/core/c4/splice/identity/interior.js +80 -0
  182. package/dist/core/c4/splice/identity/interior.js.map +1 -0
  183. package/dist/core/c4/splice/identity/owner.js +50 -0
  184. package/dist/core/c4/splice/identity/owner.js.map +1 -0
  185. package/dist/core/c4/splice/landscape-merge.js +154 -100
  186. package/dist/core/c4/splice/landscape-merge.js.map +1 -1
  187. package/dist/core/c4/splice/model/merge.js +140 -0
  188. package/dist/core/c4/splice/model/merge.js.map +1 -0
  189. package/dist/core/c4/splice/placement.js +14 -4
  190. package/dist/core/c4/splice/placement.js.map +1 -1
  191. package/dist/core/c4/workspace.js +2 -1
  192. package/dist/core/c4/workspace.js.map +1 -1
  193. package/dist/core/coherence/coherence.js +7 -0
  194. package/dist/core/coherence/coherence.js.map +1 -1
  195. package/dist/core/delta/delta.js +9 -0
  196. package/dist/core/delta/delta.js.map +1 -1
  197. package/dist/core/deployment/delta.js +4 -0
  198. package/dist/core/deployment/delta.js.map +1 -1
  199. package/dist/core/diff/base-state.js +21 -5
  200. package/dist/core/diff/base-state.js.map +1 -1
  201. package/dist/core/diff/victims.js +10 -1
  202. package/dist/core/diff/victims.js.map +1 -1
  203. package/dist/core/docs.js +31 -22
  204. package/dist/core/docs.js.map +1 -1
  205. package/dist/core/doctor/config.js +7 -3
  206. package/dist/core/doctor/config.js.map +1 -1
  207. package/dist/core/doctor/doctor.js +48 -9
  208. package/dist/core/doctor/doctor.js.map +1 -1
  209. package/dist/core/doctor/report.js.map +1 -1
  210. package/dist/core/doctor/reports/scan.js +242 -0
  211. package/dist/core/doctor/reports/scan.js.map +1 -0
  212. package/dist/core/explain/families.js +1 -1
  213. package/dist/core/explain/families.js.map +1 -1
  214. package/dist/core/explain/refusals.js +1 -1
  215. package/dist/core/explain/refusals.js.map +1 -1
  216. package/dist/core/explore/explore.js +44 -9
  217. package/dist/core/explore/explore.js.map +1 -1
  218. package/dist/core/fleet-context.js +47 -0
  219. package/dist/core/fleet-context.js.map +1 -1
  220. package/dist/core/gate/partners.js +5 -9
  221. package/dist/core/gate/partners.js.map +1 -1
  222. package/dist/core/kernel/ids/fqn/ancestors.js +65 -0
  223. package/dist/core/kernel/ids/fqn/ancestors.js.map +1 -0
  224. package/dist/core/pack/living.js +47 -13
  225. package/dist/core/pack/living.js.map +1 -1
  226. package/dist/core/pack/pack.js +9 -6
  227. package/dist/core/pack/pack.js.map +1 -1
  228. package/dist/core/projection/arch-slice.js +2 -2
  229. package/dist/core/projection/arch-slice.js.map +1 -1
  230. package/dist/core/repo/paths.js +40 -0
  231. package/dist/core/repo/paths.js.map +1 -1
  232. package/dist/core/repo/tree/render/projects.js +182 -0
  233. package/dist/core/repo/tree/render/projects.js.map +1 -0
  234. package/dist/core/repo/tree/render/stale.js +2 -3
  235. package/dist/core/repo/tree/render/stale.js.map +1 -1
  236. package/dist/core/repo/tree/render/views.js +51 -3
  237. package/dist/core/repo/tree/render/views.js.map +1 -1
  238. package/dist/core/repo/tree/walk.js +45 -2
  239. package/dist/core/repo/tree/walk.js.map +1 -1
  240. package/dist/core/scaffold/landscape.js +219 -9
  241. package/dist/core/scaffold/landscape.js.map +1 -1
  242. package/dist/core/staging/writes.js.map +1 -1
  243. package/dist/core/usecases/delta/claims.js +1 -0
  244. package/dist/core/usecases/delta/claims.js.map +1 -1
  245. package/dist/core/usecases/delta/overlay.js +4 -0
  246. package/dist/core/usecases/delta/overlay.js.map +1 -1
  247. package/dist/core/usecases/fleet.js +18 -6
  248. package/dist/core/usecases/fleet.js.map +1 -1
  249. package/dist/core/usecases/operations.js +7 -0
  250. package/dist/core/usecases/operations.js.map +1 -1
  251. package/dist/core/usecases/service/exclude.js +119 -0
  252. package/dist/core/usecases/service/exclude.js.map +1 -0
  253. package/dist/core/usecases/service/flows.js +161 -0
  254. package/dist/core/usecases/service/flows.js.map +1 -0
  255. package/dist/core/vocabulary/maturity.js +10 -0
  256. package/dist/core/vocabulary/maturity.js.map +1 -1
  257. package/examples/README.md +23 -0
  258. package/examples/docs/architecture/landscape.likec4 +41 -0
  259. package/examples/docs/architecture/usecases/order-notification.likec4 +5 -2
  260. package/examples/docs/likec4.config.json +0 -1
  261. package/examples/docs/services/checkout-web/model.likec4 +39 -56
  262. package/examples/docs/services/order-service/arch.spec.md +13 -6
  263. package/examples/docs/services/order-service/model.likec4 +53 -67
  264. package/examples/docs/services/payment-service/arch.spec.md +7 -4
  265. package/examples/docs/services/payment-service/model.likec4 +32 -33
  266. package/examples/docs/services/platform/identity-service/arch.spec.md +7 -4
  267. package/examples/docs/services/platform/identity-service/model.likec4 +29 -33
  268. package/examples/docs/services/platform/notification-service/health.yaml +8 -3
  269. package/examples/docs/services/platform/notification-service/model.likec4 +39 -59
  270. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"checks.js","sourceRoot":"","sources":["../../../src/core/brief/checks.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The checks half of the adoption brief: every check `loam validate` will run\n * against a baseline, each naming the invocation that surfaces it. Its\n * counterpart — the UNCHECKED list, the checks that do not exist — lives in\n * unchecked.ts: honesty no validator will ever supply, and an agent that does\n * not know where the checking stops will assume it never does.\n */\n\n/* ------------------------------------------------------------------ */\n/* The checks */\n/* ------------------------------------------------------------------ */\n\n/**\n * The two invocations a baseline meets. `<id>` is a placeholder for the service\n * id — the JSON contract keeps it symbolic, the text renderer substitutes it.\n */\nconst VIA_SERVICE = \"loam validate --service <id>\";\nexport const VIA_ALL = \"loam validate --all\";\n\nexport interface BriefCheck {\n code: string;\n severity: \"error\" | \"warn\";\n /**\n * The invocation that surfaces this check. Most run under `--service`, but\n * the fleet cross-check does not — attributing it there taught agents to\n * expect a finding that run never reports.\n */\n via: typeof VIA_SERVICE | typeof VIA_ALL;\n what: string;\n}\n\n/** What `loam validate` will run against the result — each check names the invocation that surfaces it. */\nexport const VALIDATE_CHECKS: BriefCheck[] = [\n { code: \"service.no-model\", severity: \"error\", via: VIA_SERVICE, what: \"there is no model.likec4 — every other check stops here\" },\n { code: \"c4.invalid\", severity: \"error\", via: VIA_SERVICE, what: \"model.likec4 does not parse as LikeC4\" },\n {\n code: \"requirements.missing-scenarios\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a requirement in spec.md or arch.spec.md has no `#### Scenario:`\",\n },\n {\n code: \"spec.duplicate-requirement\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"one `### Requirement:` name defined twice in one living spec.md or arch.spec.md — a later merge edits only the first, the rest live on as stale copies\",\n },\n {\n code: \"spec.requirement-id-invalid\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"an optional `Requirement-ID:` violates `[A-Za-z][A-Za-z0-9._-]{0,127}` — stable identities must be portable and unambiguous\",\n },\n {\n code: \"spec.requirement-id-repeated\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"one requirement declares `Requirement-ID:` more than once — keep exactly one identity line\",\n },\n {\n code: \"spec.requirement-id-duplicate\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"one stable Requirement-ID identifies multiple requirements in one spec.md or arch.spec.md\",\n },\n // The keep-last pair: a second `Operations:` / `Covers:` line in one\n // requirement body REPLACES the first — assignment, not append — so the\n // author's two-line list silently loses its first line. The semantics stay;\n // the loss no longer happens in silence.\n {\n code: \"spec.repeated-operations\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a second `Operations:` line in one requirement body — the last replaces the earlier ones, whose list is silently dropped; merge into one comma-separated line\",\n },\n {\n code: \"spec.repeated-covers\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a second `Covers:` line in one requirement body — same keep-last rule, same silent loss; merge into one comma-separated line\",\n },\n // The graded absences: the brief marks spec.md and openapi.yaml required, and\n // validate agrees they belong — as warns, because partial adoption is a\n // supported state and these are its honest progress meter, not a gate.\n {\n code: \"service.no-spec\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"spec.md does not exist yet — legal mid-adoption, but requirement coverage and API governance are unchecked until it does\",\n },\n {\n // `warn` is the floor, not the whole story, and the difference matters to a\n // baseline: the absence is graded an ERROR once something already written\n // down joins into the file — a living requirement's `Operations:` line, or\n // an op-linked landscape edge (`landscape.expects`, landscape.ts). For a\n // service the fleet already calls, `required: true` on this target and this\n // row are the same fact stated twice.\n code: \"service.no-openapi\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"openapi.yaml does not exist yet — quiet only when the landscape proves no other service calls an operation on this one, and an ERROR rather than a warning once a living `Operations:` line or a landscape edge already names an operation it would have defined\",\n },\n {\n code: \"openapi.invalid\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"openapi.yaml exists but does not parse — an unreadable contract proves nothing, so the `api.*` checks and the spine's op resolution are suspended until it reads\",\n },\n {\n code: \"api.ungoverned\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"an operationId in openapi.yaml that no requirement's `Operations:` line names\",\n },\n {\n code: \"api.ops-unlinked\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"openapi.yaml defines operations and spec.md has requirements, but no `Operations:` line joins them — every cross-axis check is vacuously green\",\n },\n // The `Operations:` spine, checked against the LIVING contract. It used to\n // fire only inside a feature delta, so a baseline could ship a requirement\n // governing an operation nobody defines and stay green until the first\n // feature touched it.\n {\n code: \"spec-api.op-undefined\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a living requirement's `Operations:` line names an operationId this service's openapi.yaml does not define — the two axes disagree about what the service exposes\",\n },\n {\n code: \"spec.no-requirements\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"spec.md exists but holds no `### Requirement:` block at all — every requirement-driven check below it is vacuous rather than passing\",\n },\n // The deprecation pair fires on a fresh baseline too — a legacy service is\n // exactly where ops marked `deprecated: true` coexist with their\n // replacements, and documenting that lifecycle honestly is part of the\n // adoption. Mark the flag where the code retires an op; loam reads it, and\n // has no removal semantics beyond it.\n {\n code: \"api.requirement-deprecated\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a requirement whose `Operations:` list resolves only to operations openapi.yaml marks `deprecated: true` — the behaviour it governs is being retired\",\n },\n {\n code: \"spine.op-undefined\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a landscape edge into this service calls an operation its openapi.yaml does not define — a broken contract between services\",\n },\n {\n code: \"spine.op-link-missing\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: 'a landscape \"Calls\" edge into this service with no `metadata { op }`',\n },\n {\n code: \"spine.op-deprecated\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a landscape edge into this service calls an operation openapi.yaml marks `deprecated: true` — the consumer should be migrating off it\",\n },\n {\n code: \"covers.unknown\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a `Covers:` entry in arch.spec.md that resolves to no element, edge, alert or SLI — a typo silently costs the coverage it was written for\",\n },\n {\n code: \"health.invalid\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"health.yaml exists but does not parse — its alert/SLI ids are unreadable, so `Covers: alert:/sli:` entries and health coverage go unchecked until it reads (a missing health.yaml stays silent)\",\n },\n {\n code: \"health.uncovered\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"an alert or SLI declared in health.yaml that no arch.spec.md requirement covers — expected until the arch spec is written; each one is a signal nothing tests\",\n },\n {\n code: \"health.dependency-unmodelled\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a health.yaml `dependencies:` id that nothing in this service's OWN model.likec4 answers to (element id, `metadata { service }` binding, or title) — the model carries everything the service touches, private stores included as nested containers, and the on-call file must name the same world\",\n },\n {\n code: \"frontmatter.malformed\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"the frontmatter block does not parse as YAML — owner, status and sources are unreadable, and the field checks are suspended until the header is fixed\",\n },\n {\n code: \"frontmatter.field-mismatch\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"spec.md declares a different `service:` than the directory it lives under\",\n },\n {\n code: \"frontmatter.status-unknown\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a status outside `draft` / `verified` — a typo here reads as unverified forever\",\n },\n { code: \"frontmatter.field-missing\", severity: \"warn\", via: VIA_SERVICE, what: \"no `owner`, `status` or `service`\" },\n { code: \"sources.absent\", severity: \"warn\", via: VIA_SERVICE, what: \"spec.md names no `sources`\" },\n {\n code: \"sources.path-missing\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a listed source does not exist, or is a glob pattern — no longer supported (checked when loam runs inside the service's repo)\",\n },\n {\n code: \"sources.unvouched\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"`sources` with no digest — nobody has vouched for the document yet. Expected on a fresh baseline; only a human closes it\",\n },\n {\n code: \"sources.unwalked\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"`sources` leaves whole top-level paths of the service repo untouched — the finding names them (checked when loam runs inside the service's repo, against the files git tracks). It grades the WALK, not the writing: the only completeness signal loam can compute, because it is the only one that compares the document against the repository rather than against itself\",\n },\n // sources.stale, sources.current and content.stale are deliberately absent:\n // all three compare against digests `loam vouch` stamps, and a fresh baseline\n // has no stamp to compare — the table lists what a baseline can actually meet.\n // The gherkin.* staleness chain is absent the same way: it fires only once\n // `loam gherkin` has generated a suite under <gherkinDir>/loam/ in the\n // service's own repo, and a fresh baseline has not generated one.\n {\n code: \"landscape.service-unmodelled\",\n severity: \"error\",\n via: VIA_ALL,\n what: \"nothing in architecture/landscape.likec4 resolves to services/<id>/ — the fleet map is incomplete until an element exists or an existing one is bound with `metadata { service '<id>' }`\",\n },\n {\n code: \"landscape.missing\",\n severity: \"error\",\n via: VIA_ALL,\n what: \"architecture/landscape.likec4 does not exist at all — an error as soon as services/ holds one service (a warning only in an empty docs repo): with no fleet map, every cross-service check is blind rather than passing\",\n },\n {\n code: \"landscape.invalid\",\n severity: \"error\",\n via: VIA_ALL,\n what: \"architecture/landscape.likec4 exists but does not parse — fix it before anything else; the whole cross-service layer is unchecked until it reads\",\n },\n {\n code: \"landscape.binding-unknown\",\n severity: \"error\",\n via: VIA_ALL,\n what: \"an element's `metadata { service }` names a directory that does not exist — a binding is a claim about this repo, and this one is false\",\n },\n {\n code: \"landscape.binding-duplicate\",\n severity: \"warn\",\n via: VIA_ALL,\n what: \"two landscape elements resolve to the same services/<id>/ — every element→service join picks one of them arbitrarily, so the other's edges are filed under a service that does not own them\",\n },\n];\n"]}
1
+ {"version":3,"file":"checks.js","sourceRoot":"","sources":["../../../src/core/brief/checks.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The checks half of the adoption brief: every check `loam validate` will run\n * against a baseline, each naming the invocation that surfaces it. Its\n * counterpart — the UNCHECKED list, the checks that do not exist — lives in\n * unchecked.ts: honesty no validator will ever supply, and an agent that does\n * not know where the checking stops will assume it never does.\n */\n\n/* ------------------------------------------------------------------ */\n/* The checks */\n/* ------------------------------------------------------------------ */\n\n/**\n * The two invocations a baseline meets. `<id>` is a placeholder for the service\n * id — the JSON contract keeps it symbolic, the text renderer substitutes it.\n */\nconst VIA_SERVICE = \"loam validate --service <id>\";\nexport const VIA_ALL = \"loam validate --all\";\n\nexport interface BriefCheck {\n code: string;\n severity: \"error\" | \"warn\";\n /**\n * The invocation that surfaces this check. Most run under `--service`, but\n * the fleet cross-check does not — attributing it there taught agents to\n * expect a finding that run never reports.\n */\n via: typeof VIA_SERVICE | typeof VIA_ALL;\n what: string;\n}\n\n/** What `loam validate` will run against the result — each check names the invocation that surfaces it. */\nexport const VALIDATE_CHECKS: BriefCheck[] = [\n { code: \"service.no-model\", severity: \"error\", via: VIA_SERVICE, what: \"there is no model.likec4 — every other check stops here\" },\n {\n code: \"c4.invalid\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"model.likec4 does not parse as LikeC4 — for a model that extends the fleet map, parsed together with `architecture/landscape.likec4`, and every error is this model's, including one the parser blames on the map (the map parses clean on its own)\",\n },\n // The two shapes' own grades. A model that extends the map cannot diverge from\n // it — there is one declaration — and a model that stands alone cannot own an\n // element outside itself, because nothing else is in its project. So exactly\n // one of these two can ever fire for a given model, and which one is decided\n // by the file's own grammar (SCHEMA.md, \"Two shapes of a service model\").\n {\n code: \"c4.declaration-diverged\",\n severity: \"warn\",\n via: VIA_SERVICE,\n // \"the fleet map (architecture/)\", never one file: the map is a project, an\n // element may be declared in any document of it, and the finding's own\n // message and `loam explain` both refuse to name a file for that reason.\n // Naming `architecture/landscape.likec4` here sent a reader of a fleet whose\n // map is spread over several documents to a file that may not declare the\n // element at all (verification 2026-09-04).\n what: \"a model that declares its own `specification` (the standalone shape) declares an element the fleet map (`architecture/`) also declares, and the two copies disagree about `kind`, `title`, the tag SET or the `metadata { service }` binding — two documents are two authorities on one element. `description` is deliberately not compared. Copy the map's declaration verbatim, or migrate the model to extend the map\",\n },\n {\n code: \"c4.element-unowned\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a model that extends the fleet map declares an element OUTSIDE the element that resolves to this service — a system this service reaches belongs in `architecture/landscape.likec4`, declared once, and another service's internals belong in that service's model\",\n },\n {\n code: \"requirements.missing-scenarios\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a requirement in spec.md or arch.spec.md has no `#### Scenario:`\",\n },\n {\n code: \"spec.duplicate-requirement\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"one `### Requirement:` name defined twice in one living spec.md or arch.spec.md — a later merge edits only the first, the rest live on as stale copies\",\n },\n {\n code: \"spec.requirement-id-invalid\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"an optional `Requirement-ID:` violates `[A-Za-z][A-Za-z0-9._-]{0,127}` — stable identities must be portable and unambiguous\",\n },\n {\n code: \"spec.requirement-id-repeated\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"one requirement declares `Requirement-ID:` more than once — keep exactly one identity line\",\n },\n {\n code: \"spec.requirement-id-duplicate\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"one stable Requirement-ID identifies multiple requirements in one spec.md or arch.spec.md\",\n },\n // The keep-last pair: a second `Operations:` / `Covers:` line in one\n // requirement body REPLACES the first — assignment, not append — so the\n // author's two-line list silently loses its first line. The semantics stay;\n // the loss no longer happens in silence.\n {\n code: \"spec.repeated-operations\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a second `Operations:` line in one requirement body — the last replaces the earlier ones, whose list is silently dropped; merge into one comma-separated line\",\n },\n {\n code: \"spec.repeated-covers\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a second `Covers:` line in one requirement body — same keep-last rule, same silent loss; merge into one comma-separated line\",\n },\n // The graded absences: the brief marks spec.md and openapi.yaml required, and\n // validate agrees they belong — as warns, because partial adoption is a\n // supported state and these are its honest progress meter, not a gate.\n {\n code: \"service.no-spec\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"spec.md does not exist yet — legal mid-adoption, but requirement coverage and API governance are unchecked until it does\",\n },\n {\n // `warn` is the floor, not the whole story, and the difference matters to a\n // baseline: the absence is graded an ERROR once something already written\n // down joins into the file — a living requirement's `Operations:` line, or\n // an op-linked landscape edge (`landscape.expects`, landscape.ts). For a\n // service the fleet already calls, `required: true` on this target and this\n // row are the same fact stated twice.\n code: \"service.no-openapi\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"openapi.yaml does not exist yet — quiet only when the landscape proves no other service calls an operation on this one, and an ERROR rather than a warning once a living `Operations:` line or a landscape edge already names an operation it would have defined\",\n },\n {\n code: \"openapi.invalid\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"openapi.yaml exists but does not parse — an unreadable contract proves nothing, so the `api.*` checks and the spine's op resolution are suspended until it reads\",\n },\n {\n code: \"api.ungoverned\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"an operationId in openapi.yaml that no requirement's `Operations:` line names\",\n },\n {\n code: \"api.ops-unlinked\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"openapi.yaml defines operations and spec.md has requirements, but no `Operations:` line joins them — every cross-axis check is vacuously green\",\n },\n // The `Operations:` spine, checked against the LIVING contract. It used to\n // fire only inside a feature delta, so a baseline could ship a requirement\n // governing an operation nobody defines and stay green until the first\n // feature touched it.\n {\n code: \"spec-api.op-undefined\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a living requirement's `Operations:` line names an operationId this service's openapi.yaml does not define — the two axes disagree about what the service exposes\",\n },\n {\n code: \"spec.no-requirements\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"spec.md exists but holds no `### Requirement:` block at all — every requirement-driven check below it is vacuous rather than passing\",\n },\n // The deprecation pair fires on a fresh baseline too — a legacy service is\n // exactly where ops marked `deprecated: true` coexist with their\n // replacements, and documenting that lifecycle honestly is part of the\n // adoption. Mark the flag where the code retires an op; loam reads it, and\n // has no removal semantics beyond it.\n {\n code: \"api.requirement-deprecated\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a requirement whose `Operations:` list resolves only to operations openapi.yaml marks `deprecated: true` — the behaviour it governs is being retired\",\n },\n {\n code: \"spine.op-undefined\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a landscape edge into this service calls an operation its openapi.yaml does not define — a broken contract between services\",\n },\n {\n code: \"spine.op-link-missing\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: 'a landscape \"Calls\" edge into this service with no `metadata { op }`',\n },\n {\n code: \"spine.op-deprecated\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a landscape edge into this service calls an operation openapi.yaml marks `deprecated: true` — the consumer should be migrating off it\",\n },\n {\n code: \"covers.unknown\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a `Covers:` entry in arch.spec.md that resolves to no element, edge, alert or SLI — a typo silently costs the coverage it was written for\",\n },\n {\n code: \"health.invalid\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"health.yaml exists but does not parse — its alert/SLI ids are unreadable, so `Covers: alert:/sli:` entries and health coverage go unchecked until it reads (a missing health.yaml stays silent)\",\n },\n {\n code: \"health.uncovered\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"an alert or SLI declared in health.yaml that no arch.spec.md requirement covers — expected until the arch spec is written; each one is a signal nothing tests\",\n },\n {\n code: \"health.dependency-unmodelled\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a health.yaml `dependencies:` id that nothing in this service's OWN model.likec4 answers to (element id, `metadata { service }` binding, or title) — the model carries everything the service touches, private stores included as nested containers, and the on-call file must name the same world\",\n },\n {\n code: \"frontmatter.malformed\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"the frontmatter block does not parse as YAML — owner, status and sources are unreadable, and the field checks are suspended until the header is fixed\",\n },\n {\n code: \"frontmatter.field-mismatch\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"spec.md declares a different `service:` than the directory it lives under\",\n },\n {\n code: \"frontmatter.status-unknown\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a status outside `draft` / `verified` — a typo here reads as unverified forever\",\n },\n { code: \"frontmatter.field-missing\", severity: \"warn\", via: VIA_SERVICE, what: \"no `owner`, `status` or `service`\" },\n { code: \"sources.absent\", severity: \"warn\", via: VIA_SERVICE, what: \"spec.md names no `sources`\" },\n {\n code: \"sources.path-missing\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a listed source does not exist, or is a glob pattern — no longer supported (checked when loam runs inside the service's repo)\",\n },\n {\n code: \"sources.unvouched\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"`sources` with no digest — nobody has vouched for the document yet. Expected on a fresh baseline; only a human closes it\",\n },\n {\n code: \"sources.unwalked\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"`sources` leaves whole top-level paths of the service repo untouched — the finding names them (checked when loam runs inside the service's repo, against the files git tracks). It grades the WALK, not the writing: the only completeness signal loam can compute, because it is the only one that compares the document against the repository rather than against itself\",\n },\n // sources.stale, sources.current and content.stale are deliberately absent:\n // all three compare against digests `loam vouch` stamps, and a fresh baseline\n // has no stamp to compare — the table lists what a baseline can actually meet.\n // The gherkin.* staleness chain is absent the same way: it fires only once\n // `loam gherkin` has generated a suite under <gherkinDir>/loam/ in the\n // service's own repo, and a fresh baseline has not generated one.\n //\n // The service's OWN use cases: a `dynamic view` in any `.likec4` beside\n // model.likec4 (or inside it) that opts in with `#req-`. The same codes the\n // fleet target grades over architecture/usecases/, reused on the service\n // target because the flow's hops name this service's containers, which only\n // its own project can resolve. `usecase.step-unlinked` was at first left\n // out, on the reasoning that a hop between two containers of one service\n // owes no operationId — true, and exactly the scope of the guard that keeps\n // it quiet there (a service owes none to itself); but a service's model\n // routinely declares a stand-in for a sibling service it calls, and a hop\n // from a service-local flow into that stand-in with no `metadata { op }`\n // warns exactly as it does on the fleet map. The row is listed so the\n // agent's own baseline run never reports a code the brief left unexplained.\n {\n code: \"usecase.step-unbacked\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a hop of a `#req-`-tagged dynamic view in this service's project that no relationship in model.likec4 backs — LikeC4 reports nothing for it, so only this does\",\n },\n {\n code: \"usecase.step-contested\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"two or more relationships back one hop of a service-local flow and name different operations — loam names the candidates rather than picking one\",\n },\n {\n code: \"usecase.step-unlinked\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a hop of a service-local flow into ANOTHER service's element — a stand-in this model.likec4 declares for a sibling, bound or titled as that service — backed by an edge carrying no `metadata { op }` and no `publishes`/`consumes`; never on a hop whose caller and provider resolve to the same service, because a service owes no operationId to itself\",\n },\n {\n code: \"usecase.requirement-unresolved\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a `#req-` tag on a service-local flow naming no `Requirement-ID` of this service's spec.md or arch.spec.md — the id is scoped by the directory, never by a capability\",\n },\n {\n code: \"usecase.capability-unresolved\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a `#cap-` tag on a flow inside this service's own project — a capability is claimed on the fleet map, never inside one service; drop the tag and keep the `#req-` one\",\n },\n {\n code: \"usecase.flow-invalid\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a `.likec4` beside model.likec4 that does not parse as part of the service's LikeC4 project — no flow in it was graded; model.likec4 itself is still graded alone\",\n },\n {\n code: \"landscape.service-unmodelled\",\n severity: \"error\",\n via: VIA_ALL,\n what: \"nothing in architecture/landscape.likec4 resolves to services/<id>/ — the fleet map is incomplete until an element exists or an existing one is bound with `metadata { service '<id>' }`. On the FLEET target for any service; on the SERVICE target too when that service's model.likec4 EXTENDS the map, where an unbound directory leaves the model with nothing to be inside and nothing in it can be graded\",\n },\n {\n code: \"landscape.missing\",\n severity: \"error\",\n via: VIA_ALL,\n what: \"architecture/landscape.likec4 does not exist at all — an error as soon as services/ holds one service (a warning only in an empty docs repo): with no fleet map, every cross-service check is blind rather than passing\",\n },\n {\n code: \"landscape.invalid\",\n severity: \"error\",\n via: VIA_ALL,\n // The fleet map is the whole `architecture/` PROJECT, so this code fires for\n // a palette or a use case beside the landscape just as readily as for the\n // landscape itself — and the message names the document that broke. Saying\n // \"landscape.likec4\" here sent an author to a file with no errors in it.\n what: \"the `architecture/` project does not parse — the landscape, or any `.likec4` beside it; the message names the document that broke. Fix it before anything else: the whole cross-service layer is unchecked until the map reads\",\n },\n {\n code: \"landscape.binding-unknown\",\n severity: \"error\",\n via: VIA_ALL,\n what: \"an element's `metadata { service }` names a directory that does not exist — a binding is a claim about this repo, and this one is false\",\n },\n {\n code: \"landscape.binding-duplicate\",\n severity: \"warn\",\n via: VIA_ALL,\n what: \"two landscape elements resolve to the same services/<id>/ — every element→service join picks one of them arbitrarily, so the other's edges are filed under a service that does not own them\",\n },\n // Evidence-gated: the model's own cross-boundary calls are the proof that\n // the map owes an edge, so a service whose model declares none stays silent\n // — that state is the adopt brief's to name (`landscape.touched`), never a\n // fleet finding that cannot tell a hermit from a batch job. And BINARY: one\n // edge closes it, because loam cannot tell a call the map forgot from one the\n // fleet deliberately does not draw at its own altitude, and a set difference\n // reported as an omission is an invented edge with a code beside it.\n { code: \"landscape.service-isolated\", severity: \"warn\", via: VIA_ALL, what: \"an element resolves to services/<id>/ and no edge anywhere in architecture/ touches it, while model.likec4 declares a call across its boundary — the service is drawn and invisible to every cross-service check; silent when the model declares no such call, and silent once ONE edge is drawn: the check is touched/untouched, not a set difference over the attested calls\" },\n // The four renderer-wiring grades, all repaired by one `loam subsystem sync`.\n // None of them fires on a baseline written from this brief: the model it asks\n // for extends the map, so it belongs to the root project and is owed no file\n // and no exclusion. They are listed because an agent adopting into an OLDER\n // repository — one whose root `likec4.config.json` still excludes\n // `services/**`, or that carries a hand-written project file — meets them on\n // the first `--all` run and must not read one as work on the model.\n {\n code: \"service.likec4-config-missing\",\n severity: \"warn\",\n via: VIA_ALL,\n what: \"a model that stands alone (declares its own `specification`) renders only as a project of its own, and services/<id>/likec4.config.json is not there — `loam subsystem sync` writes it; without it the model is a box on the fleet map with nothing renderable inside it. Never raised for a model that extends the map\",\n },\n {\n code: \"service.likec4-config-stray\",\n severity: \"warn\",\n via: VIA_ALL,\n what: \"the reverse: model.likec4 extends the fleet map and a likec4.config.json sits beside it — the renderer registers that file as a project of its own, holding nothing, or holding the model alone where it cannot parse. Delete it; loam never writes one beside an extending model\",\n },\n {\n code: \"service.model-excluded\",\n severity: \"warn\",\n via: VIA_ALL,\n what: \"model.likec4 extends the fleet map and the root likec4.config.json's `exclude` hides it, so the renderer never loads it. Two entry shapes reach it: one covering the DIRECTORY, which `loam subsystem sync` rewrites (that command maintains the `services/` entries), and one shaped like a FILE (`services/**/model.likec4`), which sync cannot repair — the message says which one you have, and the file shape has to be removed or narrowed by hand\",\n },\n {\n code: \"service.model-unexcluded\",\n severity: \"warn\",\n via: VIA_ALL,\n what: \"the mirror, and the damaging one: a model that stands alone whose directory the root `exclude` does NOT cover — the renderer merges it into the map, reports every kind and element it declares as a duplicate, and blanks the whole root project, not just this service. Same repair\",\n },\n // The same list, one level up: the entry covers the MAP. It is not a\n // per-service wiring grade and no `sync` repairs it — sync recomputes the\n // `services/` entries only — so it is listed separately from the four above.\n {\n code: \"landscape.excluded\",\n severity: \"warn\",\n via: VIA_ALL,\n what: \"the root likec4.config.json's `exclude` covers architecture/landscape.likec4 itself — the renderer loads no fleet map at all, and every extending model in the root project resolves against nothing, while loam keeps grading the map it reads from disk. `c4.fleet-project-invalid` is not graded while the entry stands, because that check reads the renderer's project and would report this one line once per model. The finding quotes the entry verbatim; remove or narrow it by hand, because `loam subsystem sync` maintains only the `services/` entries in that list\",\n },\n {\n code: \"c4.fleet-project-invalid\",\n severity: \"warn\",\n via: VIA_ALL,\n what: \"every document reads clean where loam grades it, and the ONE project the renderer builds out of them — the map, every extending model, and every `.likec4` beside one — does not parse. A tag or an element declared in two of those documents is declared twice there; declare it once, in the map or in the single service that owns it. Not graded at all while `landscape.excluded` holds: with the map out of that project every `extend` is unresolvable, so the entry is the finding\",\n },\n];\n"]}
@@ -1,194 +1,277 @@
1
1
  /**
2
- * The fleet-map half of the adoption brief: what `architecture/landscape.likec4`
2
+ * The fleet-map half of the adoption brief, READ side: what the fleet map
3
3
  * already says about one service — its elements, its inbound and outbound
4
- * edges, the operations the fleet already calls on it — and the block the
5
- * service still owes the file when nothing in it resolves to the service yet.
6
- * Both halves exist to stop an agent inventing a parallel model: the fleet has
7
- * already drawn some of these boxes and edges, and a baseline has to attach to
8
- * them.
4
+ * edges, the operations the fleet already calls on it — and which of the four
5
+ * states the map is in for it. The write the service still owes the file — the
6
+ * block, when nothing in it resolves to the service yet, or the EDGES, when an
7
+ * element does and no edge touches it — is spelled in `./map/owed.ts`
8
+ * (`landscapeArtifact`, `instructionFor`), which this module hands its findings
9
+ * to. Both halves exist to stop an agent inventing a parallel model: the fleet
10
+ * has already drawn some of these boxes and edges, and a baseline has to attach.
11
+ *
12
+ * "The fleet map" is the `architecture/` PROJECT — the landscape plus every
13
+ * other `.likec4` under it, minus the generated views file — and not the
14
+ * landscape FILE. The two are the same document set `validate --all` grades
15
+ * `landscape.service-isolated` over, and reading one file here let the two
16
+ * surfaces disagree about the same tree.
9
17
  */
10
18
  import { existsSync } from "node:fs";
11
19
  import { loadFile } from "../c4/likec4.js";
12
- import { elementService, serviceResolver } from "../c4/resolve/service.js";
20
+ import { loadArchitecture } from "../c4/project/architecture.js";
21
+ import { attestedCalls } from "../c4/resolve/attested.js";
22
+ import { serviceResolver } from "../c4/resolve/service.js";
23
+ import { ServiceModels } from "../c4/prefetch/fleet.js";
24
+ import { OBLIGATION_TAG_PREFIX } from "../obligations/obligations.js";
13
25
  import { landscapePath } from "../repo/paths.js";
14
26
  import { enumeratedServiceIds, serviceTreePathOf } from "../repo/service-target.js";
27
+ import { EXTERNAL_TAG, GRADED_TAGS } from "../vocabulary/maturity.js";
28
+ import { brokenDocuments, instructionFor } from "./map/owed.js";
29
+ import { properAncestorIds } from "../kernel/ids/fqn/ancestors.js";
15
30
  /**
16
- * The LikeC4 identifier the fleet map's element for a service conventionally
17
- * carries: the directory name, camel-cased. It is a suggestion and nothing
18
- * more — `metadata { service '<id>' }` is what actually joins the element to
19
- * the directory — but a brief that hands over a concrete block gets followed,
20
- * and one that describes a block in prose gets improvised on.
31
+ * The service ids the resolver may fall back on when an element carries no
32
+ * `metadata { service }` binding — the same positive evidence `validate` and
33
+ * `list` hand it. A docs repo with no `services/` yet (`adopt` runs there)
34
+ * leaves the resolver with bindings alone, which is what it had before.
21
35
  */
22
- function elementIdFor(service) {
23
- const parts = service.split(/[^A-Za-z0-9]+/).filter((p) => p !== "");
24
- if (parts.length === 0)
25
- return "service";
26
- const head = parts[0].toLowerCase();
27
- const tail = parts.slice(1).map((p) => p[0].toUpperCase() + p.slice(1).toLowerCase());
28
- const id = head + tail.join("");
29
- // LikeC4 identifiers cannot open with a digit; a service id can.
30
- return /^[0-9]/.test(id) ? `svc${id[0].toUpperCase()}${id.slice(1)}` : id;
36
+ async function knownServices(docsDir) {
37
+ return new Set(await enumeratedServiceIds(docsDir));
31
38
  }
32
39
  /**
33
- * The block this service still owes `architecture/landscape.likec4`.
34
- *
35
- * `expects` is what the fleet ALREADY calls on the service — those edges exist
36
- * in the map today, so the example draws the outbound side as a comment rather
37
- * than telling an agent to duplicate an edge that is already there.
40
+ * The tags loam reads on an ELEMENT. `GRADED_TAGS` is the vocabulary's own
41
+ * list of the tags that change what loam concludes — spelled once there, so
42
+ * the day a third tag is graded this set follows and the instruction below
43
+ * does not tell an agent to clear it as unread. The obligation prefix is the
44
+ * one tag PREFIX loam reads on an element; `#cap-`/`#req-` are view tags and
45
+ * never sit on one, so they are not in this list.
38
46
  */
39
- function landscapeBlock(service, expects, present) {
40
- const id = elementIdFor(service);
41
- const calls = expects.length > 0
42
- ? expects.map((op) => `// <caller> -> ${id} 'Calls ${op}' { metadata { op '${op}' } }`)
43
- : [`// <caller> -> ${id} 'Calls <operationId>' { metadata { op '<operationId>' } }`];
44
- const model = [
45
- "model {",
46
- ...(present ? [" // ... the fleet's other services stay exactly as they are ...", ""] : []),
47
- ` ${id} = softwareSystem '${service}' {`,
48
- " description '<one line: what this service is responsible for>'",
49
- ` metadata { service '${service}' }`,
50
- " }",
51
- "",
52
- " // every call in or out of it, one edge each:",
53
- ...calls.map((line) => ` ${line}`),
54
- "}",
55
- ];
56
- // When the file does not exist the example is the WHOLE file, so it carries
57
- // the `specification` block a bare `model` would be rejected without: the
58
- // brief must never hand over a document `loam validate` refuses. When the file
59
- // does exist the example is a fragment to splice, and repeating a
60
- // `specification` block there would invite a second one.
61
- return present
62
- ? model.join("\n")
63
- : ["specification {", " element softwareSystem", "}", "", ...model].join("\n");
64
- }
65
- /** The fleet map's own brief — assembled per service, because the block it owes names the service. */
66
- export function landscapeArtifact(service, expects, present, servicePath) {
67
- return {
68
- artifact: "landscape.likec4",
69
- required: true,
70
- purpose: "the fleet map — until an element here resolves to this directory, the service is documented and invisible to every cross-service check",
71
- shape: [
72
- "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.",
73
- `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).`,
74
- "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.",
75
- "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.",
76
- "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.",
77
- ],
78
- example: landscapeBlock(service, expects, present),
79
- };
80
- }
47
+ const LOAM_TAGS = new Set(GRADED_TAGS);
48
+ const LOAM_TAG_PREFIXES = [OBLIGATION_TAG_PREFIX];
81
49
  /**
82
- * The service ids the resolver may fall back on when an element carries no
83
- * `metadata { service }` binding — the same positive evidence `validate` and
84
- * `list` hand it. A docs repo that cannot be enumerated (no `services/` yet:
85
- * `adopt` runs there) leaves the resolver with bindings alone, which is what it
86
- * had before.
50
+ * The element's tags loam does NOT read — named in the instruction and never
51
+ * interpreted. A placeholder convention ("drawn ahead of adoption") is the
52
+ * fleet's own, `loam seed` writes none, and a view written `exclude
53
+ * element.tag = #<that>` keeps an adopted service hidden until it is cleared.
87
54
  */
88
- async function knownServices(docsDir) {
89
- return new Set(await enumeratedServiceIds(docsDir));
55
+ function foreignTagsOf(elements) {
56
+ const unread = (t) => !LOAM_TAGS.has(t) && !LOAM_TAG_PREFIXES.some((p) => t.startsWith(p));
57
+ return [...new Set(elements.flatMap((e) => e.tags))].filter(unread);
90
58
  }
91
59
  /**
92
- * What the living landscape already says about this service.
60
+ * The attested calls, or nothing off a model that could not be read: `c4.invalid`
61
+ * is validate's finding, and an edge list read off half a document is invention.
93
62
  *
94
- * This is the half of the brief that stops an agent inventing a parallel model:
95
- * the fleet has already drawn some of these boxes and edges, and the baseline has
96
- * to attach to them. A landscape that does not parse yields `modelled: null` —
97
- * "nothing models it" would be a claim about a document nobody could read.
63
+ * Through the ONE reader of `model.likec4` (`core/c4/service-model/load.ts`),
64
+ * because the two shapes are read differently and this brief is handed to an
65
+ * agent that will act on it: a model that EXTENDS the fleet map, opened as a
66
+ * lone file, comes back as a pile of parse errors — so the brief would tell
67
+ * every migrated service that its model attests nothing and quietly drop the
68
+ * edges the map most likely owes. `mapUnreadable` is the same empty answer for
69
+ * the same reason: an extending model whose map does not parse was never read.
70
+ */
71
+ async function attestedFrom(docsDir, paths, service, known) {
72
+ // The memo directly rather than through `core/fleet-context.ts`, and the
73
+ // reason is the package graph: this module already depends on `core/c4/`, and
74
+ // reaching for the read index would add a `brief -> core-root` edge for one
75
+ // read in a command that grades a single service. The three things the memo
76
+ // cannot know for itself are supplied here — the enumeration this function
77
+ // was already handed, the per-file loader, and a counter nobody is keeping.
78
+ const models = new ServiceModels({
79
+ onProjectLoad: () => {
80
+ // `adopt` reports no read counters, so there is nothing to increment. The
81
+ // hook is the memo's way of letting a caller own its own statistics, and
82
+ // an empty one is that answer, not a forgotten line.
83
+ },
84
+ known: async () => known,
85
+ standalone: (path) => loadFile(path),
86
+ });
87
+ const model = await models.model(docsDir, paths);
88
+ if (model.mapUnreadable || model.doc.errors.length > 0)
89
+ return [];
90
+ return attestedCalls(model.doc, service, known);
91
+ }
92
+ /**
93
+ * What the living fleet map already says about this service — the half of the
94
+ * brief that stops an agent inventing a parallel model: the fleet has already
95
+ * drawn some of these boxes and edges, and the baseline has to attach to them.
96
+ * An `architecture/` project that does not parse yields `modelled: null` —
97
+ * "nothing models it" would be a claim about documents nobody could read; the
98
+ * landscape FILE parsing on its own does not rescue it, because the map the
99
+ * renderer and `validate --all` see is the whole project. `paths` is the
100
+ * service's own artifact set, located by the caller through the enumeration
101
+ * (never joined at `services/<id>/`): the model is opened in exactly one state —
102
+ * an element with no edge — to name the calls the map most likely owes. The
103
+ * WHOLE record travels rather than the model path alone, because the model is
104
+ * now read through its own shape and an extending one is read as a project of
105
+ * the service's directory plus the map. It is required rather than optional
106
+ * because the edgeless instruction states what that file attests, and a caller
107
+ * that omitted it would get a brief asserting a fact about a document loam never
108
+ * opened.
98
109
  */
99
- export async function landscapeContext(docsDir, service) {
110
+ export async function landscapeContext(docsDir, service, paths) {
100
111
  const path = landscapePath(docsDir);
101
112
  // The service's own directory, spelled from the enumeration rather than joined
102
113
  // at the root: this text is handed to an AGENT that will go and edit files, and
103
114
  // a filed service named at `services/<id>/` sends it to create a second
104
115
  // directory beside the one that already exists.
105
116
  const servicePath = await serviceTreePathOf(docsDir, service);
117
+ const bare = { service, expects: [], servicePath, attested: [], foreignTags: [] };
106
118
  const empty = {
107
119
  present: existsSync(path),
108
120
  parses: false,
121
+ callersKnown: false,
109
122
  modelled: null,
110
123
  elements: [],
111
124
  inbound: [],
112
125
  outbound: [],
113
126
  expects: [],
127
+ touched: null,
128
+ attested: [],
114
129
  instruction: null,
115
130
  };
116
131
  if (!empty.present)
117
- return { ...empty, modelled: false, instruction: instructionFor(service, "absent", [], servicePath) };
118
- const land = await loadFile(path);
119
- if (land.errors.length > 0)
120
- return { ...empty, instruction: instructionFor(service, "unparseable", [], servicePath) };
121
- const mine = (e) => elementService(e) === service;
122
- const elements = land.elements.filter(mine).map((e) => ({
132
+ return { ...empty, modelled: false, instruction: instructionFor({ ...bare, state: "absent" }) };
133
+ // The `architecture/` PROJECT, through the one loader every other reader
134
+ // shares — never `loadFile(path)`. A fleet spreads its model over more than
135
+ // the landscape (a second `model { }` block in a sibling, a use case, a
136
+ // palette), and `landscape.service-isolated` grades `touched` over exactly
137
+ // this set: reading the landscape alone made `adopt` report `touched: false`,
138
+ // the attested calls and an edit target for a service the fleet run on the
139
+ // same tree called matched, with nothing to tell an agent which was right.
140
+ const known = await knownServices(docsDir);
141
+ const land = await loadArchitecture(docsDir);
142
+ if (land.errors.length > 0) {
143
+ // Since W4 this arm answers for the whole `architecture/` PROJECT, so the
144
+ // document that broke is very often a SIBLING beside a landscape that parses
145
+ // perfectly — and two things followed from not saying so:
146
+ // - the instruction told an agent to go and fix parse errors in
147
+ // `architecture/landscape.likec4`, which has none, contradicting
148
+ // `validate --all` run one second later on the same tree;
149
+ // - the API question — who CALLS this service — was answered "unknown"
150
+ // off a landscape file loam can read, so a broken palette flipped
151
+ // `openapi.yaml` to required for a browser UI the map proves nobody
152
+ // calls, while `list` and `context` (which read the FILE, see
153
+ // core/pack/living.ts) answered the opposite about the same tree.
154
+ // Nothing else is rescued: `modelled`, `elements` and `touched` stay
155
+ // unknown, because those are claims about the project the renderer loads.
156
+ const expects = await expectsFromFile(path, service, known);
157
+ return {
158
+ ...empty,
159
+ callersKnown: expects !== null,
160
+ expects: expects ?? [],
161
+ instruction: instructionFor({
162
+ ...bare,
163
+ expects: expects ?? [],
164
+ state: "unparseable",
165
+ broken: brokenDocuments(docsDir, land),
166
+ }),
167
+ };
168
+ }
169
+ // One resolver for the whole scan, built with the real service ids, and
170
+ // built BEFORE the element filter: "an element resolves to this service" is
171
+ // the resolver's answer — a nearest-ancestor binding wins over a descendant's
172
+ // title — and it is the answer `validate --all` grades `service-unmodelled`
173
+ // on and `loam context` prints `modelled` from. This used to ask each
174
+ // element for its OWN binding-or-title instead, so a container titled
175
+ // 'payment-service' inside an element bound to order-service read as
176
+ // payment-service's box, and the edgeless arm told an agent "do not add a
177
+ // second element" for a service the fleet run reported unmodelled (error).
178
+ // The same resolver serves the edges: one drawn into a modelled container
179
+ // (`paymentService.api`) is an edge into the service, and `expects` — which
180
+ // decides whether this service owes an OpenAPI contract at all — has to see
181
+ // it. Building it once also stops the per-edge rebuild the old
182
+ // `serviceOf(land.elements, id)` call did.
183
+ const svcOf = serviceResolver(land.elements, known);
184
+ const mine = (id) => svcOf(id) === service;
185
+ // The service-LEVEL elements only: one that resolves to the service while
186
+ // an ancestor does too is a container of it, and listing it would show a
187
+ // second box the map does not have.
188
+ const elements = land.elements
189
+ .filter((e) => mine(e.id) && !properAncestorIds(e.id).some(mine))
190
+ .map((e) => ({
123
191
  id: e.id,
124
192
  title: e.title,
125
193
  kind: e.kind,
126
194
  tags: e.tags,
127
195
  bound: e.service !== undefined,
128
196
  }));
129
- // One resolver for the whole scan, built with the real service ids: an edge
130
- // drawn into a modelled container (`paymentService.api`) is an edge into the
131
- // service, and `expects` — which now decides whether this service owes an
132
- // OpenAPI contract at all — has to see it. Building it once also stops the
133
- // per-edge rebuild the old `serviceOf(land.elements, id)` call did.
134
- const svcOf = serviceResolver(land.elements, await knownServices(docsDir));
135
197
  const inbound = [];
136
198
  const outbound = [];
199
+ // An edge whose BOTH endpoints resolve here is the service's own internal
200
+ // wiring: filed as inbound it made the service its own caller (verification
201
+ // 2026-09-04). It still TOUCHES the service, so `touched` below reads the
202
+ // relationships rather than these lists — the isolation check counts either
203
+ // endpoint, and the two predicates must not disagree.
204
+ const self = (r) => mine(r.source) && mine(r.target);
137
205
  for (const r of land.relationships) {
206
+ if (self(r))
207
+ continue;
138
208
  const edge = { op: r.op ?? null, title: r.title ?? null };
139
- if (svcOf(r.target) === service)
209
+ if (mine(r.target))
140
210
  inbound.push({ from: svcOf(r.source), ...edge });
141
- else if (svcOf(r.source) === service)
211
+ else if (mine(r.source))
142
212
  outbound.push({ to: svcOf(r.target), ...edge });
143
213
  }
144
214
  const expects = [...new Set(inbound.map((e) => e.op).filter((op) => op !== null))];
215
+ const modelled = land.elements.some((e) => mine(e.id));
216
+ const touched = modelled ? land.relationships.some((r) => mine(r.source) || mine(r.target)) : null;
217
+ const attested = touched === false && existsSync(paths.model) ? await attestedFrom(docsDir, paths, service, known) : [];
218
+ const req = {
219
+ service,
220
+ expects,
221
+ servicePath,
222
+ attested,
223
+ foreignTags: foreignTagsOf(elements),
224
+ elementId: elements[0]?.id,
225
+ // The one graded tag that changes what the brief may PROMISE: an `#external`
226
+ // element is never `landscape.service-isolated`'s subject. Off the same
227
+ // element `elementId` names, never the union.
228
+ external: elements[0]?.tags.includes(EXTERNAL_TAG) ?? false,
229
+ };
230
+ const instruction = !modelled
231
+ ? instructionFor({ ...req, state: "unmodelled" })
232
+ : touched === false
233
+ ? instructionFor({ ...req, state: "edgeless" })
234
+ : null;
145
235
  return {
146
236
  present: true,
147
237
  parses: true,
148
- modelled: elements.length > 0,
238
+ callersKnown: true,
239
+ modelled,
149
240
  elements,
150
241
  inbound,
151
242
  outbound,
152
243
  expects,
153
- instruction: elements.length > 0 ? null : instructionFor(service, "unmodelled", expects, servicePath),
244
+ touched,
245
+ attested,
246
+ instruction,
154
247
  };
155
248
  }
156
249
  /**
157
- * The fleet-map instruction, in the three states a landscape can be in.
250
+ * The operations the fleet already calls on this service, read off the landscape
251
+ * FILE alone — `null` when that file cannot be read either.
158
252
  *
159
- * It says the file, the block, and the consequence, in that order. The
160
- * consequence is the part agents acted on: "add an element" reads as tidiness,
161
- * "until you do, `loam validate --all` fails and no cross-service check can see
162
- * this service" reads as work.
253
+ * The fallback for an `architecture/` project that does not parse, and the
254
+ * narrowest one that can be justified: it answers ONE question, the one `loam
255
+ * list` and `loam context` answer off the same bytes (`landscapeEvidence`,
256
+ * core/vocabulary/maturity.ts). "Nothing calls this service" and "loam could not
257
+ * tell" are different answers, and only the first may switch an API contract
258
+ * off, which is why the empty list and the unreadable file are told apart here
259
+ * rather than collapsed to `[]`.
163
260
  */
164
- function instructionFor(service, state, expects, servicePath) {
165
- const id = elementIdFor(service);
166
- const bind = `an element bound with \`metadata { service '${service}' }\``;
167
- const consequence = `Until one resolves, \`loam validate --all\` reports \`landscape.service-unmodelled\` (error) for ` +
168
- `${servicePath}/: the service is documented and invisible — no edge into it can be checked ` +
169
- `against its openapi.yaml, and no feature can draw a call to it.`;
170
- // `expects` is reachable only through elements that already resolve to this
171
- // service, so it is empty in every state that produces an instruction. It is
172
- // threaded through anyway rather than dropped: the caller computes it once,
173
- // and the day an unmodelled service can inherit an edge (a container-level
174
- // binding, say) the sentence is already here instead of being noticed missing.
175
- const owed = expects.length > 0
176
- ? ` The fleet already calls ${expects.map((o) => `'${o}'`).join(", ")} on this service, so those edges exist; your element is what they will resolve to.`
177
- : "";
178
- if (state === "absent") {
179
- return (`architecture/landscape.likec4 does not exist yet. Create it with a \`specification { ... }\` block ` +
180
- `declaring the kinds you use and a \`model { ... }\` block containing ${bind} ` +
181
- `(conventionally \`${id} = softwareSystem '${service}'\`), plus one edge per cross-service call, each ` +
182
- `carrying \`metadata { op '<operationId>' }\`. ${consequence}${owed}`);
261
+ async function expectsFromFile(path, service, known) {
262
+ try {
263
+ const doc = await loadFile(path);
264
+ if (doc.errors.length > 0)
265
+ return null;
266
+ const svcOf = serviceResolver(doc.elements, known);
267
+ const ops = doc.relationships
268
+ .filter((r) => svcOf(r.target) === service)
269
+ .map((r) => r.op)
270
+ .filter((op) => op !== undefined);
271
+ return [...new Set(ops)];
183
272
  }
184
- if (state === "unparseable") {
185
- return (`architecture/landscape.likec4 exists but does not parse (\`landscape.invalid\`), so nothing can be said ` +
186
- `about what it already models. Fix the parse errors first — they blind every cross-service check at once — ` +
187
- `then make sure it holds ${bind}. ${consequence}`);
273
+ catch {
274
+ return null;
188
275
  }
189
- return (`Nothing in architecture/landscape.likec4 resolves to ${servicePath}/. ADD to that file — do not rewrite ` +
190
- `it, every other service's map is in there — ${bind}, conventionally ` +
191
- `\`${id} = softwareSystem '${service}'\`, inside the existing \`model { ... }\` block, and draw each ` +
192
- `cross-service call as an edge carrying \`metadata { op '<operationId>' }\`. ${consequence}${owed}`);
193
276
  }
194
277
  //# sourceMappingURL=landscape.js.map