@ybotok/loam 0.2.0-alpha.5 → 0.2.0-alpha.7

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 (303) hide show
  1. package/CHANGELOG.md +1063 -0
  2. package/MIGRATING-from-OpenSpec.md +7 -4
  3. package/QUICKSTART.md +8 -3
  4. package/README.md +13 -7
  5. package/ROADMAP.md +285 -11
  6. package/SCHEMA.md +566 -45
  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 +53 -23
  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/commit.js +34 -1
  25. package/dist/commands/seed/commit.js.map +1 -1
  26. package/dist/commands/seed/plan.js +158 -13
  27. package/dist/commands/seed/plan.js.map +1 -1
  28. package/dist/commands/seed/seed.js +7 -2
  29. package/dist/commands/seed/seed.js.map +1 -1
  30. package/dist/commands/show/feature.js +2 -1
  31. package/dist/commands/show/feature.js.map +1 -1
  32. package/dist/commands/show/marks.js +3 -5
  33. package/dist/commands/show/marks.js.map +1 -1
  34. package/dist/commands/show/service.js +39 -18
  35. package/dist/commands/show/service.js.map +1 -1
  36. package/dist/commands/subsystem/edit.js +4 -2
  37. package/dist/commands/subsystem/edit.js.map +1 -1
  38. package/dist/commands/subsystem/move.js +55 -17
  39. package/dist/commands/subsystem/move.js.map +1 -1
  40. package/dist/commands/subsystem/sync.js +238 -87
  41. package/dist/commands/subsystem/sync.js.map +1 -1
  42. package/dist/commands/subsystem/txn/exclude.js +132 -0
  43. package/dist/commands/subsystem/txn/exclude.js.map +1 -0
  44. package/dist/commands/subsystem/txn/report.js +174 -0
  45. package/dist/commands/subsystem/txn/report.js.map +1 -0
  46. package/dist/commands/subsystem/txn/txn.js +126 -22
  47. package/dist/commands/subsystem/txn/txn.js.map +1 -1
  48. package/dist/commands/subsystem/txn/views.js +159 -19
  49. package/dist/commands/subsystem/txn/views.js.map +1 -1
  50. package/dist/commands/validate/arch-coverage.js +16 -9
  51. package/dist/commands/validate/arch-coverage.js.map +1 -1
  52. package/dist/commands/validate/checks/fleet-shape.js +30 -77
  53. package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
  54. package/dist/commands/validate/checks/health-deps.js +9 -6
  55. package/dist/commands/validate/checks/health-deps.js.map +1 -1
  56. package/dist/commands/validate/checks/vocabulary.js +4 -19
  57. package/dist/commands/validate/checks/vocabulary.js.map +1 -1
  58. package/dist/commands/validate/feature.js +1 -1
  59. package/dist/commands/validate/feature.js.map +1 -1
  60. package/dist/commands/validate/fleet/census.js +37 -13
  61. package/dist/commands/validate/fleet/census.js.map +1 -1
  62. package/dist/commands/validate/fleet/landscape.js +131 -120
  63. package/dist/commands/validate/fleet/landscape.js.map +1 -1
  64. package/dist/commands/validate/fleet/load.js +66 -0
  65. package/dist/commands/validate/fleet/load.js.map +1 -1
  66. package/dist/commands/validate/fleet/map/attest.js +102 -0
  67. package/dist/commands/validate/fleet/map/attest.js.map +1 -0
  68. package/dist/commands/validate/fleet/map/bindings.js +109 -0
  69. package/dist/commands/validate/fleet/map/bindings.js.map +1 -0
  70. package/dist/commands/validate/fleet/map/consumers.js +219 -0
  71. package/dist/commands/validate/fleet/map/consumers.js.map +1 -0
  72. package/dist/commands/validate/fleet/map/isolation.js +116 -0
  73. package/dist/commands/validate/fleet/map/isolation.js.map +1 -0
  74. package/dist/commands/validate/fleet/map/usecases.js +56 -0
  75. package/dist/commands/validate/fleet/map/usecases.js.map +1 -0
  76. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
  77. package/dist/commands/validate/fleet/usecases/steps.js +49 -17
  78. package/dist/commands/validate/fleet/usecases/steps.js.map +1 -1
  79. package/dist/commands/validate/fleet/views/fleet-project.js +217 -0
  80. package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -0
  81. package/dist/commands/validate/fleet/views/ids.js +45 -15
  82. package/dist/commands/validate/fleet/views/ids.js.map +1 -1
  83. package/dist/commands/validate/fleet/views/projects.js +258 -70
  84. package/dist/commands/validate/fleet/views/projects.js.map +1 -1
  85. package/dist/commands/validate/fleet/views/root-documents.js +99 -0
  86. package/dist/commands/validate/fleet/views/root-documents.js.map +1 -0
  87. package/dist/commands/validate/fleet/views/stale.js +32 -15
  88. package/dist/commands/validate/fleet/views/stale.js.map +1 -1
  89. package/dist/commands/validate/service/model/diverged.js +226 -0
  90. package/dist/commands/validate/service/model/diverged.js.map +1 -0
  91. package/dist/commands/validate/service/model/grade.js +163 -0
  92. package/dist/commands/validate/service/model/grade.js.map +1 -0
  93. package/dist/commands/validate/service/service.js +110 -34
  94. package/dist/commands/validate/service/service.js.map +1 -1
  95. package/dist/commands/validate/service/specs.js +39 -5
  96. package/dist/commands/validate/service/specs.js.map +1 -1
  97. package/dist/commands/validate/service/spine.js +41 -3
  98. package/dist/commands/validate/service/spine.js.map +1 -1
  99. package/dist/commands/validate/service/usecases/tags.js +113 -0
  100. package/dist/commands/validate/service/usecases/tags.js.map +1 -0
  101. package/dist/commands/validate/service/usecases/usecases.js +209 -0
  102. package/dist/commands/validate/service/usecases/usecases.js.map +1 -0
  103. package/dist/commands/validate/validate.js +10 -24
  104. package/dist/commands/validate/validate.js.map +1 -1
  105. package/dist/core/agent/agents-md/artifacts.js +1 -0
  106. package/dist/core/agent/agents-md/artifacts.js.map +1 -1
  107. package/dist/core/agent/agents-md/command-map.js +19 -5
  108. package/dist/core/agent/agents-md/command-map.js.map +1 -1
  109. package/dist/core/agent/agents-md/cycle.js +24 -9
  110. package/dist/core/agent/agents-md/cycle.js.map +1 -1
  111. package/dist/core/agent/agents-md/map/lenses/context.js +4 -3
  112. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
  113. package/dist/core/agent/agents-md/map/subsystem.js +70 -17
  114. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
  115. package/dist/core/agent/agents-md/map/usecases.js +45 -3
  116. package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
  117. package/dist/core/agent/agents-md/refusals.js +5 -3
  118. package/dist/core/agent/agents-md/refusals.js.map +1 -1
  119. package/dist/core/agent/agents-md.js +26 -7
  120. package/dist/core/agent/agents-md.js.map +1 -1
  121. package/dist/core/agent/support/report.js +39 -5
  122. package/dist/core/agent/support/report.js.map +1 -1
  123. package/dist/core/agent/workflows/adopt.js +123 -14
  124. package/dist/core/agent/workflows/adopt.js.map +1 -1
  125. package/dist/core/agent/workflows/check.js +34 -20
  126. package/dist/core/agent/workflows/check.js.map +1 -1
  127. package/dist/core/agent/workflows/closing.js +3 -1
  128. package/dist/core/agent/workflows/closing.js.map +1 -1
  129. package/dist/core/agent/workflows/reference/spine.js +12 -6
  130. package/dist/core/agent/workflows/reference/spine.js.map +1 -1
  131. package/dist/core/brief/brief.js +88 -19
  132. package/dist/core/brief/brief.js.map +1 -1
  133. package/dist/core/brief/checks.js +133 -7
  134. package/dist/core/brief/checks.js.map +1 -1
  135. package/dist/core/brief/landscape.js +212 -129
  136. package/dist/core/brief/landscape.js.map +1 -1
  137. package/dist/core/brief/map/extend.js +81 -0
  138. package/dist/core/brief/map/extend.js.map +1 -0
  139. package/dist/core/brief/map/owed.js +284 -0
  140. package/dist/core/brief/map/owed.js.map +1 -0
  141. package/dist/core/brief/targets.js +94 -29
  142. package/dist/core/brief/targets.js.map +1 -1
  143. package/dist/core/brief/unchecked.js +13 -3
  144. package/dist/core/brief/unchecked.js.map +1 -1
  145. package/dist/core/c4/likec4.js +23 -1
  146. package/dist/core/c4/likec4.js.map +1 -1
  147. package/dist/core/c4/parsed/deployment.js +5 -3
  148. package/dist/core/c4/parsed/deployment.js.map +1 -1
  149. package/dist/core/c4/parsed/view-ids.js +39 -1
  150. package/dist/core/c4/parsed/view-ids.js.map +1 -1
  151. package/dist/core/c4/prefetch/fleet.js +142 -0
  152. package/dist/core/c4/prefetch/fleet.js.map +1 -0
  153. package/dist/core/c4/project/architecture.js +77 -7
  154. package/dist/core/c4/project/architecture.js.map +1 -1
  155. package/dist/core/c4/project/batch.js +140 -0
  156. package/dist/core/c4/project/batch.js.map +1 -0
  157. package/dist/core/c4/project/documents.js +12 -6
  158. package/dist/core/c4/project/documents.js.map +1 -1
  159. package/dist/core/c4/project/load.js +85 -42
  160. package/dist/core/c4/project/load.js.map +1 -1
  161. package/dist/core/c4/project/staged.js +53 -15
  162. package/dist/core/c4/project/staged.js.map +1 -1
  163. package/dist/core/c4/resolve/attested.js +0 -0
  164. package/dist/core/c4/resolve/attested.js.map +1 -0
  165. package/dist/core/c4/resolve/service.js +8 -17
  166. package/dist/core/c4/resolve/service.js.map +1 -1
  167. package/dist/core/c4/resolve/steps.js +18 -0
  168. package/dist/core/c4/resolve/steps.js.map +1 -1
  169. package/dist/core/c4/root-project/exclude.js +352 -0
  170. package/dist/core/c4/root-project/exclude.js.map +1 -0
  171. package/dist/core/c4/service-model/documents.js +47 -0
  172. package/dist/core/c4/service-model/documents.js.map +1 -0
  173. package/dist/core/c4/service-model/fleet/extending.js +82 -0
  174. package/dist/core/c4/service-model/fleet/extending.js.map +1 -0
  175. package/dist/core/c4/service-model/load.js +165 -0
  176. package/dist/core/c4/service-model/load.js.map +1 -0
  177. package/dist/core/c4/service-model/renderer.js +128 -0
  178. package/dist/core/c4/service-model/renderer.js.map +1 -0
  179. package/dist/core/c4/service-model/shape.js +126 -0
  180. package/dist/core/c4/service-model/shape.js.map +1 -0
  181. package/dist/core/c4/service-model/slice.js +188 -0
  182. package/dist/core/c4/service-model/slice.js.map +1 -0
  183. package/dist/core/c4/source-scan.js +26 -0
  184. package/dist/core/c4/source-scan.js.map +1 -1
  185. package/dist/core/c4/splice/authored-source.js +60 -13
  186. package/dist/core/c4/splice/authored-source.js.map +1 -1
  187. package/dist/core/c4/splice/contract.js.map +1 -1
  188. package/dist/core/c4/splice/identity/declared.js +87 -0
  189. package/dist/core/c4/splice/identity/declared.js.map +1 -0
  190. package/dist/core/c4/splice/identity/edges.js +17 -0
  191. package/dist/core/c4/splice/identity/edges.js.map +1 -1
  192. package/dist/core/c4/splice/identity/existing.js +80 -0
  193. package/dist/core/c4/splice/identity/existing.js.map +1 -0
  194. package/dist/core/c4/splice/identity/interior.js +80 -0
  195. package/dist/core/c4/splice/identity/interior.js.map +1 -0
  196. package/dist/core/c4/splice/identity/owner.js +50 -0
  197. package/dist/core/c4/splice/identity/owner.js.map +1 -0
  198. package/dist/core/c4/splice/landscape-merge.js +154 -100
  199. package/dist/core/c4/splice/landscape-merge.js.map +1 -1
  200. package/dist/core/c4/splice/model/merge.js +140 -0
  201. package/dist/core/c4/splice/model/merge.js.map +1 -0
  202. package/dist/core/c4/splice/placement.js +14 -4
  203. package/dist/core/c4/splice/placement.js.map +1 -1
  204. package/dist/core/c4/workspace.js +2 -1
  205. package/dist/core/c4/workspace.js.map +1 -1
  206. package/dist/core/coherence/coherence.js +7 -0
  207. package/dist/core/coherence/coherence.js.map +1 -1
  208. package/dist/core/delta/delta.js +9 -0
  209. package/dist/core/delta/delta.js.map +1 -1
  210. package/dist/core/deployment/delta.js +16 -10
  211. package/dist/core/deployment/delta.js.map +1 -1
  212. package/dist/core/diff/base-state.js +21 -5
  213. package/dist/core/diff/base-state.js.map +1 -1
  214. package/dist/core/diff/victims.js +10 -1
  215. package/dist/core/diff/victims.js.map +1 -1
  216. package/dist/core/docs.js +29 -24
  217. package/dist/core/docs.js.map +1 -1
  218. package/dist/core/doctor/config.js +7 -3
  219. package/dist/core/doctor/config.js.map +1 -1
  220. package/dist/core/doctor/doctor.js +36 -8
  221. package/dist/core/doctor/doctor.js.map +1 -1
  222. package/dist/core/doctor/report.js.map +1 -1
  223. package/dist/core/doctor/reports/scan.js +242 -0
  224. package/dist/core/doctor/reports/scan.js.map +1 -0
  225. package/dist/core/explain/refusals.js +1 -1
  226. package/dist/core/explain/refusals.js.map +1 -1
  227. package/dist/core/explore/explore.js +44 -9
  228. package/dist/core/explore/explore.js.map +1 -1
  229. package/dist/core/fleet-context.js +47 -0
  230. package/dist/core/fleet-context.js.map +1 -1
  231. package/dist/core/gate/checks.js +5 -1
  232. package/dist/core/gate/checks.js.map +1 -1
  233. package/dist/core/gate/partners.js +65 -26
  234. package/dist/core/gate/partners.js.map +1 -1
  235. package/dist/core/kernel/ids/fqn/ancestors.js +65 -0
  236. package/dist/core/kernel/ids/fqn/ancestors.js.map +1 -0
  237. package/dist/core/pack/living.js +47 -13
  238. package/dist/core/pack/living.js.map +1 -1
  239. package/dist/core/pack/pack.js +9 -6
  240. package/dist/core/pack/pack.js.map +1 -1
  241. package/dist/core/projection/arch-slice.js +2 -2
  242. package/dist/core/projection/arch-slice.js.map +1 -1
  243. package/dist/core/repo/paths.js +45 -38
  244. package/dist/core/repo/paths.js.map +1 -1
  245. package/dist/core/repo/tree/render/body.js +45 -6
  246. package/dist/core/repo/tree/render/body.js.map +1 -1
  247. package/dist/core/repo/tree/render/projects.js +93 -35
  248. package/dist/core/repo/tree/render/projects.js.map +1 -1
  249. package/dist/core/repo/tree/render/service/header.js +125 -0
  250. package/dist/core/repo/tree/render/service/header.js.map +1 -0
  251. package/dist/core/repo/tree/render/service/peers.js +28 -0
  252. package/dist/core/repo/tree/render/service/peers.js.map +1 -0
  253. package/dist/core/repo/tree/render/service/view-id.js +67 -0
  254. package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
  255. package/dist/core/repo/tree/render/service/views.js +78 -0
  256. package/dist/core/repo/tree/render/service/views.js.map +1 -0
  257. package/dist/core/repo/tree/render/stale.js +20 -4
  258. package/dist/core/repo/tree/render/stale.js.map +1 -1
  259. package/dist/core/repo/tree/render/views.js +149 -32
  260. package/dist/core/repo/tree/render/views.js.map +1 -1
  261. package/dist/core/repo/tree/walk.js +45 -2
  262. package/dist/core/repo/tree/walk.js.map +1 -1
  263. package/dist/core/scaffold/landscape.js +219 -9
  264. package/dist/core/scaffold/landscape.js.map +1 -1
  265. package/dist/core/staging/writes.js.map +1 -1
  266. package/dist/core/usecases/delta/claims.js +1 -0
  267. package/dist/core/usecases/delta/claims.js.map +1 -1
  268. package/dist/core/usecases/delta/overlay.js +4 -0
  269. package/dist/core/usecases/delta/overlay.js.map +1 -1
  270. package/dist/core/usecases/fleet.js +18 -6
  271. package/dist/core/usecases/fleet.js.map +1 -1
  272. package/dist/core/usecases/operations.js +7 -0
  273. package/dist/core/usecases/operations.js.map +1 -1
  274. package/dist/core/usecases/service/exclude.js +119 -0
  275. package/dist/core/usecases/service/exclude.js.map +1 -0
  276. package/dist/core/usecases/service/flows.js +161 -0
  277. package/dist/core/usecases/service/flows.js.map +1 -0
  278. package/dist/core/verify/checklist.js +42 -10
  279. package/dist/core/verify/checklist.js.map +1 -1
  280. package/dist/core/vocabulary/issue.js.map +1 -1
  281. package/dist/core/vocabulary/maturity.js +51 -1
  282. package/dist/core/vocabulary/maturity.js.map +1 -1
  283. package/examples/README.md +30 -8
  284. package/examples/docs/architecture/landscape.likec4 +47 -0
  285. package/examples/docs/architecture/services.likec4 +70 -0
  286. package/examples/docs/architecture/subsystems.likec4 +3 -0
  287. package/examples/docs/architecture/usecases/order-notification.likec4 +5 -2
  288. package/examples/docs/likec4.config.json +0 -1
  289. package/examples/docs/services/checkout-web/model.likec4 +39 -56
  290. package/examples/docs/services/order-service/arch.spec.md +13 -6
  291. package/examples/docs/services/order-service/model.likec4 +58 -70
  292. package/examples/docs/services/payment-service/arch.spec.md +7 -4
  293. package/examples/docs/services/payment-service/model.likec4 +38 -39
  294. package/examples/docs/services/platform/identity-service/arch.spec.md +7 -4
  295. package/examples/docs/services/platform/identity-service/model.likec4 +35 -37
  296. package/examples/docs/services/platform/notification-service/health.yaml +8 -3
  297. package/examples/docs/services/platform/notification-service/model.likec4 +43 -64
  298. package/package.json +1 -1
  299. package/examples/docs/services/checkout-web/likec4.config.json +0 -4
  300. package/examples/docs/services/order-service/likec4.config.json +0 -4
  301. package/examples/docs/services/payment-service/likec4.config.json +0 -4
  302. package/examples/docs/services/platform/identity-service/likec4.config.json +0 -4
  303. package/examples/docs/services/platform/notification-service/likec4.config.json +0 -4
@@ -3,7 +3,6 @@
3
3
  "title": "Fleet landscape",
4
4
  "exclude": [
5
5
  "**/node_modules/**",
6
- "services/**",
7
6
  "features/**"
8
7
  ]
9
8
  }
@@ -1,27 +1,24 @@
1
- // Per-service C4 for a UI service. A UI is a first-class C4 system: it has
2
- // containers, it has runtime dependencies, and its pages (ui/pages/*.page.yaml)
3
- // hang off the operations the edges below name.
1
+ // checkout-web's own C4 — what is INSIDE the element the fleet map binds to
2
+ // services/checkout-web/. A UI is a first-class C4 system: it has containers, it
3
+ // has runtime dependencies, and its pages (ui/pages/*.page.yaml) hang off the
4
+ // operations the edges below name.
4
5
  //
5
- // Every .likec4 file is parsed ON ITS OWN — loam never concatenates them — so
6
- // this document declares the kinds it uses even though architecture/landscape.likec4
7
- // declares the same ones. Leaving them out here does not inherit, it fails to parse
8
- // (`c4.invalid`, an error), and the spec/API joins that read the model go quiet with it.
9
- specification {
10
- element softwareSystem
11
- element container
12
- element person
13
- }
14
-
6
+ // An EXTENDING model: no `specification` block, no partner copies. The kinds and
7
+ // every element this file points at — `customer`, `marketplace.orderService`,
8
+ // `marketplace.paymentService` — are declared once in
9
+ // architecture/landscape.likec4, and the root `likec4.config.json` does not
10
+ // exclude `services/**`, so the map and every model are read as ONE LikeC4
11
+ // project. `extend` takes the fully-qualified id, because the element is nested
12
+ // in the `marketplace` group.
13
+ //
14
+ // Note what this file does NOT do: it does not declare `customer = person`. The
15
+ // actor is the map's, and the edge below simply points at it. That is the rule
16
+ // the shape turns on — an element declared here outside
17
+ // `marketplace.checkoutWeb` is `c4.element-unowned` — and it is also what stops
18
+ // two documents from holding two different descriptions of the same person.
19
+ // SCHEMA.md, "Two shapes of a service model", has the other shape.
15
20
  model {
16
- // The actor belongs in a UI's own model in a way it does not in a backend's:
17
- // the pages exist for this person, and the flows in ui/pages/ start at them.
18
- // The landscape draws the same customer -> checkout-web edge at fleet scale;
19
- // a person is exempt from the service/directory cross-check either way.
20
- customer = person 'Customer'
21
-
22
- checkoutWeb = softwareSystem 'checkout-web' {
23
- description 'Customer-facing checkout UI'
24
-
21
+ extend marketplace.checkoutWeb {
25
22
  spa = container 'spa' {
26
23
  description 'Checkout SPA — cart, payment and order status pages'
27
24
  technology 'React'
@@ -37,37 +34,21 @@ model {
37
34
  technology 'Node.js'
38
35
  }
39
36
 
40
- customer -> spa 'Buys'
41
37
  spa -> bff 'Calls the checkout BFF'
42
38
  }
43
39
 
44
- // The services this one actually calls. A service model carries everything the
45
- // service touches, cross-boundary included, drawn with the edge the service
46
- // really has — the landscape carries only the boundary crossing (checkout-web ->
47
- // order-service), while the edge below says WHICH container makes the call.
48
- // Without these three edges this file would be two boxes and a hop, and
49
- // `c4.no-relationships` would be right to say a model that reaches nothing is
50
- // almost never true.
51
- orderService = softwareSystem 'order-service' {
52
- description 'Owns the order lifecycle'
53
- // Bind rather than rely on the title. A renamed box silently stops matching
54
- // its directory everywhere the two are joined — in the landscape that costs
55
- // `landscape.binding-unknown` (error) when the id is wrong, and here it is
56
- // what `health.dependency-unmodelled` resolves a declared dependency
57
- // through in the services that have a health.yaml. checkout-web has none:
58
- // a UI is nobody's runtime dependency, so it declares no SLIs, no alerts
59
- // and no dependency graph, and owes no arch.spec.md to cover them.
60
- metadata {
61
- service 'order-service'
62
- }
63
- }
64
- paymentService = softwareSystem 'payment-service' {
65
- description 'Owns payment authorization/capture'
66
- metadata {
67
- service 'payment-service'
68
- }
69
- }
40
+ // The person is the MAP's element and the hop into this UI is drawn from it.
41
+ // The landscape draws the coarser `customer -> marketplace.checkoutWeb` twin
42
+ // at fleet scale; this one says which container the click reaches. A person is
43
+ // exempt from the service/directory cross-check either way.
44
+ customer -> marketplace.checkoutWeb.spa 'Buys'
70
45
 
46
+ // The services this one actually calls, drawn with the edge the service really
47
+ // has — the landscape carries only the boundary crossing (checkout-web ->
48
+ // order-service), while the edges below say WHICH container makes the call.
49
+ // Without them this file would be two boxes and a hop, and `c4.no-relationships`
50
+ // would be right to say a model that reaches nothing is almost never true.
51
+ //
71
52
  // `metadata { op }` is the C4 half of the operation join: the exact
72
53
  // operationId order-service's openapi.yaml declares. What `loam validate`
73
54
  // grades is the LANDSCAPE's twin of each edge below — `spine.op-undefined`
@@ -77,12 +58,12 @@ model {
77
58
  // nothing would catch it if they did. Two calls to one service are two edges,
78
59
  // not one edge with a list — a view has to be able to show that the status
79
60
  // page and the checkout page stand on different operations.
80
- checkoutWeb.bff -> orderService 'Calls createOrder' {
61
+ marketplace.checkoutWeb.bff -> marketplace.orderService 'Calls createOrder' {
81
62
  metadata {
82
63
  op 'createOrder'
83
64
  }
84
65
  }
85
- checkoutWeb.bff -> orderService 'Calls getOrder' {
66
+ marketplace.checkoutWeb.bff -> marketplace.orderService 'Calls getOrder' {
86
67
  metadata {
87
68
  op 'getOrder'
88
69
  }
@@ -94,16 +75,18 @@ model {
94
75
  // for an edge that SAYS it calls something and names no operation, so nothing
95
76
  // can check the call still exists. Free here and one warning there, which is
96
77
  // the asymmetry worth knowing: the fleet map is what the spine reads.
97
- checkoutWeb.spa -> paymentService 'Calls the payment API'
78
+ marketplace.checkoutWeb.spa -> marketplace.paymentService 'Calls the payment API'
98
79
  }
99
80
 
100
81
  // Views are the LikeC4 renderer's, never loam's — no check reads this block.
101
- // Scoped to the system so the page answers "what does the checkout UI stand on"
102
- // rather than redrawing the fleet, which architecture/landscape.likec4 already does.
82
+ // Scoped to the service's element so the page answers "what does the checkout UI
83
+ // stand on" rather than redrawing the fleet, which architecture/landscape.likec4
84
+ // already does. This file is part of the root project: `npx likec4 start` at the
85
+ // docs root draws it, with no per-service project file to write.
103
86
  views {
104
- view checkout_web of checkoutWeb {
87
+ view checkout_web of marketplace.checkoutWeb {
105
88
  title 'checkout-web — containers and the services they call'
106
89
  include *
107
- include orderService, paymentService
90
+ include marketplace.orderService, marketplace.paymentService
108
91
  }
109
92
  }
@@ -25,9 +25,16 @@ this service's own `model.likec4` plus the landscape (`covers.unknown`, with a d
25
25
  hint, is the typo guard — a mistyped entry otherwise costs exactly the coverage it was written
26
26
  for), and every `slis[].name` and `alerts[].name` in `health.yaml` must be named by one of them
27
27
  (`health.uncovered`, or a signal ships to the on-call rotation with nothing testing it). Edge
28
- entries below are spelled as this model draws them — `orderService.outbox -> kafka.orderEvents`
29
- — because the endpoint that matters is the container; `order-service -> payment-service`
30
- resolves too, through the `metadata { service }` binding on the root element.
28
+ entries below are spelled as this model draws them — `marketplace.orderService.outbox ->
29
+ kafka.orderEvents` — because the endpoint that matters is the container; `order-service ->
30
+ payment-service` resolves too, through the `metadata { service }` binding on the map's element.
31
+
32
+ Element ids here carry the FULL path, `marketplace.` and all. That is a consequence of the model
33
+ shape rather than a style: `model.likec4` extends the element the fleet map declares inside the
34
+ `marketplace` group, so the container it adds is `marketplace.orderService.db` and no shorter
35
+ spelling of it exists. A service whose model declares its own `specification` and is parsed alone
36
+ mints shorter ids and writes them here — see SCHEMA.md, "Two shapes of a service model", which also
37
+ lists requalifying these lines as a step of the migration between the two.
31
38
 
32
39
  ## Requirements
33
40
 
@@ -40,7 +47,7 @@ key rather than placing a second order or requesting a second authorization. che
40
47
  retry on a gateway timeout is not a rare path — it is the normal shape of a mobile checkout —
41
48
  and without the key a customer's flaky connection charges the card twice.
42
49
 
43
- Covers: orderService.db, orderService.domain -> paymentService
50
+ Covers: marketplace.orderService.db, marketplace.orderService.domain -> marketplace.paymentService
44
51
 
45
52
  #### Scenario: checkout-web retries a placement it never saw acknowledged
46
53
  - **Given** an order placed under `Idempotency-Key` "ik-8842" with an authorization already requested
@@ -63,7 +70,7 @@ database and then to kafka. A dual write has no failure mode in which both sides
63
70
  between them either announces an order nobody stored or stores one nobody hears about, and
64
71
  notification-service is the service that pays for the second case.
65
72
 
66
- Covers: orderService.outbox, orderService.outbox -> kafka.orderEvents
73
+ Covers: marketplace.orderService.outbox, marketplace.orderService.outbox -> kafka.orderEvents
67
74
 
68
75
  #### Scenario: The broker is unavailable when an order is placed
69
76
  - **Given** an accepted order whose `OrderPlaced` row is still in the outbox table
@@ -86,7 +93,7 @@ already recorded. The producer's outbox promises at-least-once delivery and a re
86
93
  redelivers whatever was in flight, so a consumer without a ledger is a consumer that confirms
87
94
  an order it already confirmed — and, once ORD-CANCEL has moved on, resurrects a cancelled one.
88
95
 
89
- Covers: orderService.consumer, kafka.paymentEvents -> orderService.consumer
96
+ Covers: marketplace.orderService.consumer, kafka.paymentEvents -> marketplace.orderService.consumer
90
97
  Consumes: payment.PaymentAuthorized
91
98
 
92
99
  #### Scenario: The same authorization is redelivered after a consumer rebalance
@@ -1,30 +1,27 @@
1
- // order-service's own C4 — the model behind `services/order-service/`.
1
+ // order-service's own C4 — what is INSIDE the element the fleet map binds to
2
+ // services/order-service/.
2
3
  //
3
- // Every .likec4 file loam reads is parsed ON ITS OWN; nothing is concatenated and
4
- // nothing is inherited from architecture/landscape.likec4. So this document declares
5
- // the kinds it uses even though the landscape declares the same names: leaving them
6
- // out does not borrow them, it fails to parse (`c4.invalid`, an error), and the arch
7
- // axis, the `Covers:` resolution and health.yaml's dependency reconciliation all go
8
- // quiet behind that one failure.
9
- specification {
10
- element softwareSystem
11
- element container
12
- element database
13
- element topic
14
- }
15
-
4
+ // This is an EXTENDING model: it declares no `specification` block, so every
5
+ // kind and tag it uses comes from architecture/landscape.likec4, and it declares
6
+ // no partner of its own. The map already says that order-service exists, what it
7
+ // is called and which directory it binds to; this file says what is inside it,
8
+ // with `extend` on the fully-qualified id the map gave that element. The id has
9
+ // to be the full path — `extend orderService` resolves to nothing, because the
10
+ // element is nested in the `marketplace` group.
11
+ //
12
+ // Nothing here re-declares kafka, payment-service or identity-service. That is
13
+ // the whole point of the shape: the root likec4.config.json does not exclude
14
+ // `services/**`, so the map and every model are ONE LikeC4 project, an element
15
+ // is declared exactly once, and two documents cannot disagree about its tags or
16
+ // its description. A PARTNER is what this file refers to and never owns — the
17
+ // map's element, reached by the id the map spells. An element declared here
18
+ // outside `marketplace.orderService` would be `c4.element-unowned`.
19
+ //
20
+ // SCHEMA.md, "Two shapes of a service model", has the other shape (a model that
21
+ // declares its own `specification` and is parsed alone) and the recipe for
22
+ // migrating between them.
16
23
  model {
17
- orderService = softwareSystem 'order-service' {
18
- description 'Owns the order lifecycle and orchestrates the payment behind it'
19
- // The binding, not the title, is what ties this element to services/order-service/,
20
- // and it is what every join downstream resolves through: health.yaml's dependency
21
- // ids, an `arch.spec.md` edge entry written as `order-service -> payment-service`,
22
- // the landscape's twin element. Rename the box without it and each of those stops
23
- // matching in silence.
24
- metadata {
25
- service 'order-service'
26
- }
27
-
24
+ extend marketplace.orderService {
28
25
  api = container 'api' {
29
26
  description 'REST API: createOrder, getOrder, cancelOrder, and the deprecated createOrderV1'
30
27
  technology 'Spring Boot'
@@ -36,7 +33,8 @@ model {
36
33
  // The two background containers exist in this model because arch.spec.md has to
37
34
  // be able to point at them: ARCH-ORD-OUTBOX and ARCH-ORD-DEDUPE cover them by id,
38
35
  // and an obligation whose subject is not modelled is an obligation no `Covers:`
39
- // line can name.
36
+ // line can name. Written the way the map spells them —
37
+ // `marketplace.orderService.outbox` — because that is the id this file makes.
40
38
  outbox = container 'outbox-relay' {
41
39
  description 'Publishes committed outbox rows to order.events.v1 — at-least-once'
42
40
  technology 'Kotlin'
@@ -45,58 +43,45 @@ model {
45
43
  description 'Applies payment.PaymentAuthorized once per paymentId, then advances the order'
46
44
  technology 'Kotlin'
47
45
  }
48
- // The store is a NESTED container of this service, never a fleet-level peer: it
49
- // has exactly one consumer, so drawn beside the services in the landscape it would
50
- // read as a system in its own right (`landscape.datastore-private` is the check
51
- // that pushes it back in here).
46
+ // The store is a NESTED container of this service, and that is this fleet's
47
+ // CHOICE rather than a rule: loam grades neither placement, and the same store
48
+ // drawn beside the services in the landscape earns no finding either. What the
49
+ // choice decides is one thing, and it is a fact about LikeC4 rather than a
50
+ // preference: an edge naming `marketplace.orderService.db` resolves only for a
51
+ // project that holds this file, and a per-service project is `architecture/`
52
+ // plus ONE model — so a store a second service will name belongs on the map,
53
+ // inside `marketplace.orderService`. Nesting hides nothing meanwhile: a second
54
+ // service reaching the same data earns `landscape.datastore-shared`, counted
55
+ // from that service's own model.
56
+ // The `database` kind is the landscape's, declared there once for every model that needs it.
52
57
  db = database 'db' {
53
58
  description 'Orders, idempotency keys, the applied-payment ledger and the outbox table'
54
59
  technology 'PostgreSQL'
55
60
  }
56
61
 
62
+ // The internal edges, written INSIDE the extend block, where the children
63
+ // resolve by their short names.
57
64
  api -> domain 'Places, reads and cancels'
58
65
  domain -> db 'Writes the order, its idempotency key and its outbox row in one transaction'
59
66
  outbox -> db 'Polls unpublished outbox rows and marks them sent'
60
67
  consumer -> db 'Records the applied paymentId, then advances the order'
61
68
  }
62
69
 
63
- // Everything the service touches from outside its own boundary. The landscape
64
- // carries only the boundary crossing; THIS file carries the whole reach, and it is
65
- // the file `health.yaml`'s `dependencies:` are reconciled against — an id nothing
66
- // here answers to is `health.dependency-unmodelled`, the check that catches a
67
- // dependency the on-call file still swears by after the code stopped calling it.
68
- paymentService = softwareSystem 'payment-service' {
69
- description 'Authorizes and captures cards'
70
- metadata {
71
- service 'payment-service'
72
- }
73
- }
74
- identityService = softwareSystem 'identity-service' {
75
- description 'Issues and introspects access tokens'
76
- metadata {
77
- service 'identity-service'
78
- }
79
- }
80
- kafka = softwareSystem 'kafka' {
81
- description 'Event backbone (platform)'
82
- // A topic per channel rather than one broker box. What a producer and a consumer
83
- // actually share is the channel, and pointing every edge at the broker turns the
84
- // fleet map into a star with one node in the middle the moment the fleet is
85
- // bigger than this example.
86
- orderEvents = topic 'order.events'
87
- paymentEvents = topic 'payment.events'
88
- }
89
-
90
- // The edges, drawn from the container that really makes each call — which is the
91
- // half of the truth the landscape cannot hold. A model with elements and no
92
- // relationships beside a health.yaml declaring two dependencies is
93
- // `c4.no-relationships`: a service that reaches nothing is almost never true.
70
+ // The cross-boundary edges, drawn OUTSIDE the extend block, from the container
71
+ // that really makes each call to the other party's element as the MAP spells
72
+ // it — which is the half of the truth the landscape cannot hold, since the map
73
+ // carries only the service-level crossing. This file names no `kafka` of its
74
+ // own: `kafka.orderEvents` is the map's topic, and pointing at it is what makes
75
+ // health.yaml's `kafka` dependency resolve (a partner brings its ancestors with
76
+ // it, so the broker answers even though only its topic is named).
94
77
  //
95
78
  // The `metadata` blocks below are not read for a service model — only landscape
96
79
  // edges feed `spine.op-*` and the event spine — but they are written anyway so the
97
80
  // two documents cannot drift into disagreeing about which operation and which
98
- // message each edge stands for.
99
- orderService.domain -> paymentService 'Calls authorizePayment' {
81
+ // message each edge stands for. A model with elements and no relationships beside
82
+ // a health.yaml declaring two dependencies is `c4.no-relationships`: a service
83
+ // that reaches nothing is almost never true.
84
+ marketplace.orderService.domain -> marketplace.paymentService 'Calls authorizePayment' {
100
85
  metadata {
101
86
  op 'authorizePayment'
102
87
  }
@@ -104,12 +89,12 @@ model {
104
89
  // ORD-CANCEL's `Requires: user/orders:cancel` has to be enforced somewhere, and this
105
90
  // is that edge: the API introspects the caller's token before it will cancel anything.
106
91
  // Drawn here and absent from health.yaml, which states the asymmetry and the reason.
107
- orderService.api -> identityService 'Calls introspectToken' {
92
+ marketplace.orderService.api -> marketplace.identityService 'Calls introspectToken' {
108
93
  metadata {
109
94
  op 'introspectToken'
110
95
  }
111
96
  }
112
- orderService.outbox -> kafka.orderEvents 'Publishes OrderPlaced' {
97
+ marketplace.orderService.outbox -> kafka.orderEvents 'Publishes OrderPlaced' {
113
98
  metadata {
114
99
  publishes 'order.OrderPlaced'
115
100
  }
@@ -119,7 +104,7 @@ model {
119
104
  // edge drawn the other way round binds to nothing. Nothing grades it in a
120
105
  // service model, which is exactly why it follows the same convention — a
121
106
  // reader who learns the arrow here should not have to unlearn it upstairs.
122
- kafka.paymentEvents -> orderService.consumer 'PaymentAuthorized advances the order' {
107
+ kafka.paymentEvents -> marketplace.orderService.consumer 'PaymentAuthorized advances the order' {
123
108
  metadata {
124
109
  consumes 'payment.PaymentAuthorized'
125
110
  }
@@ -127,12 +112,15 @@ model {
127
112
  }
128
113
 
129
114
  // Views are the LikeC4 renderer's, never loam's — no check reads this block. Scoped
130
- // to the system, because "what does an order stand on" is this file's question and
131
- // the fleet-wide one is already answered in architecture/landscape.likec4.
115
+ // to the service's element, because "what does an order stand on" is this file's
116
+ // question and the fleet-wide one is already answered in architecture/landscape.likec4.
117
+ // This file is part of the ROOT project, beside the map: `npx likec4 start` at the
118
+ // docs root draws it, and there is no per-service likec4.config.json to write.
119
+ // View ids are flat across the whole project, so this one is spelled to be unique.
132
120
  views {
133
- view order_service of orderService {
121
+ view order_service of marketplace.orderService {
134
122
  title 'order-service — containers, its store, and what it reaches'
135
123
  include *
136
- include paymentService, identityService, kafka, kafka.*
124
+ include marketplace.paymentService, marketplace.identityService, kafka, kafka.*
137
125
  }
138
126
  }
@@ -15,9 +15,12 @@ its scenarios exercise, and `loam validate` checks every entry resolves
15
15
  (`covers.unknown`) and every declared alert/SLI is covered (`health.uncovered`).
16
16
 
17
17
  An edge entry resolves by exact element id **or** by the service an endpoint stands for, which
18
- is why the entries below name `payment-service` rather than `marketplace.paymentService`: the
19
- landscape draws this service inside a grouping element, and a `Covers:` line written against a
20
- group path would have to be rewritten the day somebody moves the box.
18
+ is why the edge entries below name `payment-service` rather than `marketplace.paymentService`:
19
+ the landscape draws this service inside a grouping element, and an edge written against a group
20
+ path would have to be rewritten the day somebody moves the box. A CONTAINER has no such coarser
21
+ name, so `marketplace.paymentService.outbox` is spelled in full — `model.likec4` extends the
22
+ element the map declares inside `marketplace`, so that IS the container's id. SCHEMA.md, "Two
23
+ shapes of a service model", has the rule and the migration step that requalifies these lines.
21
24
 
22
25
  ## Requirements
23
26
 
@@ -27,7 +30,7 @@ Requirement-ID: ARCH-PAY-OUTBOX
27
30
  The service SHALL write a domain event and the state change it reports in one
28
31
  database transaction, published to kafka by an outbox relay — never a dual write.
29
32
 
30
- Covers: paymentService.outbox, payment-service -> kafka.paymentEvents
33
+ Covers: marketplace.paymentService.outbox, payment-service -> kafka.paymentEvents
31
34
  Publishes: payment.PaymentAuthorized, payment.PaymentCaptured
32
35
 
33
36
  #### Scenario: Broker down at commit time
@@ -1,21 +1,21 @@
1
- // Per-service C4. Written by an agent from the `loam adopt` brief, then curated.
1
+ // payment-service's own C4 — what is INSIDE the element the fleet map binds to
2
+ // services/payment-service/. Written by an agent from the `loam adopt` brief,
3
+ // then curated.
2
4
  //
3
- // loam parses every `.likec4` file ALONE, so this document declares its own
4
- // `specification` block; a renderer handed the whole tree at once would merge
5
- // them and call every declaration a duplicate, which is why the root
6
- // `likec4.config.json` scopes the fleet project to `architecture/` and stays
7
- // that way. What renders THIS model beside the map from the docs root is this
8
- // directory's own `likec4.config.json`, written by `loam subsystem sync`.
9
- specification {
10
- element softwareSystem
11
- element container
12
- element database
13
- }
14
-
5
+ // An EXTENDING model: no `specification` block of its own, no copy of any
6
+ // partner. The kinds (`container`, `database`) and every element this file
7
+ // points at are declared once in architecture/landscape.likec4, and the root
8
+ // `likec4.config.json` does not exclude `services/**`, so the map and every
9
+ // model are one LikeC4 project. `extend` takes the FULLY-QUALIFIED id — the
10
+ // element is nested in the `marketplace` group, so `extend paymentService`
11
+ // would resolve to nothing.
12
+ //
13
+ // There is no likec4.config.json beside this file and loam never writes one for
14
+ // an extending model: it belongs to the root project, so `npx likec4 start` at
15
+ // the docs root already draws it beside the map. SCHEMA.md, "Two shapes of a
16
+ // service model", has the other shape and the migration recipe.
15
17
  model {
16
- paymentService = softwareSystem 'payment-service' {
17
- description 'Owns payment authorization/capture'
18
-
18
+ extend marketplace.paymentService {
19
19
  api = container 'api' {
20
20
  description 'REST API'
21
21
  technology 'Spring Boot'
@@ -24,10 +24,12 @@ model {
24
24
  description 'Payment domain logic'
25
25
  technology 'Java'
26
26
  }
27
- // The store is a container of THIS service, not a fleet-level peer. Drawn
28
- // in the landscape it would be a system in its own right, available to be
29
- // depended on — which is the claim `landscape.datastore-private` exists to
30
- // refuse when only one service reaches it.
27
+ // The store is a container of THIS service rather than a fleet-level peer —
28
+ // where a store is drawn is the team's choice and loam grades neither, so
29
+ // this is a decision the example makes and explains, not one it was forced
30
+ // into. Kept here because nothing else reaches this data: the day something
31
+ // does, its edge resolves only against a declaration the MAP holds, and the
32
+ // store moves up into `marketplace.paymentService` on the landscape.
31
33
  db = database 'db' {
32
34
  description 'Payments store'
33
35
  technology 'PostgreSQL'
@@ -42,28 +44,25 @@ model {
42
44
  domain -> outbox 'Appends events in the state-change transaction'
43
45
  }
44
46
 
45
- // The service model carries everything the service touches — cross-boundary
46
- // dependencies included, drawn with the edge the service actually has. The
47
- // landscape carries only the boundary crossing; health.yaml's dependencies
48
- // must resolve HERE (`health.dependency-unmodelled` otherwise), which is why
49
- // all three of this service's declared dependencies appear below.
50
- kafka = softwareSystem 'kafka' {
51
- description 'Event backbone (platform)'
52
- }
53
- identityService = softwareSystem 'identity-service' {
54
- description 'Token validation (degradable: cached decisions carry a request)'
55
- }
56
- stripe = softwareSystem 'stripe' {
57
- description 'Card acquirer'
58
- }
59
-
60
- paymentService.outbox -> kafka 'Relay publishes payment events'
61
- paymentService.domain -> identityService 'Validates the caller token'
62
- paymentService.domain -> stripe 'Authorizes and captures cards'
47
+ // Everything the service touches across its own boundary, drawn from the
48
+ // container that really touches it to the other party's element AS THE MAP
49
+ // SPELLS IT — never a copy of that element. The landscape carries only the
50
+ // service-level crossing; these three edges say which container makes it.
51
+ // health.yaml's three dependencies resolve through them
52
+ // (`health.dependency-unmodelled` otherwise): `identity-service` through the
53
+ // binding on the map's element, `stripe` by its id, and `kafka` because a
54
+ // partner brings its ancestors — the edge names the TOPIC and the broker
55
+ // answers for it.
56
+ marketplace.paymentService.outbox -> kafka.paymentEvents 'Relay publishes payment events'
57
+ marketplace.paymentService.domain -> marketplace.identityService 'Validates the caller token'
58
+ marketplace.paymentService.domain -> stripe 'Authorizes and captures cards'
63
59
  }
64
60
 
61
+ // Views are the LikeC4 renderer's, never loam's. This file is part of the root
62
+ // project, so `npx likec4 start` at the docs root draws this view beside the
63
+ // fleet map with nothing else to write.
65
64
  views {
66
- view payment_service of paymentService {
65
+ view payment_service of marketplace.paymentService {
67
66
  include *
68
67
  }
69
68
  }
@@ -23,8 +23,11 @@ element id, an edge `source -> target`, or a health signal `sli:<id>` / `alert:<
23
23
  is `covers.unknown` (a mistyped id silently costs exactly the coverage it was written
24
24
  for), and an SLI or alert in `health.yaml` that no requirement here covers is
25
25
  `health.uncovered`. Every id below comes from `model.likec4` or `health.yaml` in this
26
- directory; nothing here reaches for the landscape, because everything this service
27
- touches is inside its own boundary.
26
+ directory; no entry names another service, because everything this service touches is
27
+ inside its own boundary. The element ids carry the full `marketplace.identityService.`
28
+ path because that is what the ids ARE: `model.likec4` extends the element the fleet map
29
+ declares inside the `marketplace` group, so a container it adds has no shorter spelling.
30
+ SCHEMA.md, "Two shapes of a service model", has the rule.
28
31
 
29
32
  ## Requirements
30
33
 
@@ -41,7 +44,7 @@ one a single-instance test never catches: the signer and the verifier are differ
41
44
  processes reading the same store, and a key promoted before it lands there makes
42
45
  one instance mint tokens the others report inactive.
43
46
 
44
- Covers: identityService.keyStore, identityService.tokens -> identityService.keyStore
47
+ Covers: marketplace.identityService.keyStore, marketplace.identityService.tokens -> marketplace.identityService.keyStore
45
48
 
46
49
  #### Scenario: A rotation keeps tokens minted a minute earlier verifiable
47
50
  - **Given** the active key K1 and tokens issued under it with thirty minutes of life left
@@ -71,7 +74,7 @@ cache holds derived state only: losing it costs latency, never correctness, and
71
74
  design where a cache miss can produce a *different verdict* is the bug this
72
75
  requirement exists to prevent.
73
76
 
74
- Covers: identityService.introspectionCache, identityService.tokens -> identityService.introspectionCache
77
+ Covers: marketplace.identityService.introspectionCache, marketplace.identityService.tokens -> marketplace.identityService.introspectionCache
75
78
 
76
79
  #### Scenario: A revocation invalidates the cached verdict immediately
77
80
  - **Given** a token whose active verdict is cached