@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
@@ -312,7 +312,13 @@ services, loam frontmatter, Operations/Covers links and feature identity still r
312
312
  specs intentionally remain `status: draft`.
313
313
  - `Operations:` links to provider OpenAPI `operationId`s, and `Covers:` links where architecture
314
314
  requirements are created.
315
- - `architecture/landscape.likec4`, service `model.likec4`, and explicit fleet relationships.
315
+ - `architecture/landscape.likec4`, service `model.likec4` (written to extend the map's element —
316
+ no `specification` block of its own; SCHEMA.md, "Two shapes of a service model"), and explicit
317
+ fleet relationships — then `loam subsystem sync`, which keeps the root `likec4.config.json`'s
318
+ `exclude` covering exactly the models that stand alone (the staged repo already carries the
319
+ root file), so every model that extends the map renders beside it; `validate --all` warns
320
+ `service.likec4-config-missing` for a standalone model still without a project file of its own,
321
+ and `service.model-unexcluded` for one the root project does not exclude.
316
322
  - Service OpenAPI contracts and provider-before-consumer sequencing where inbound edges already name
317
323
  operations.
318
324
  - LikeC4 and OpenAPI deltas for the already mapped active feature ids (`FEAT-12`, `BUG-42`, and so
package/QUICKSTART.md CHANGED
@@ -103,11 +103,16 @@ Or ask naturally: “Create a loam problem report for what just happened.” The
103
103
  relevant status and the smallest safe reproduction, and writes a separate file:
104
104
 
105
105
  ```text
106
- loam-reports/YYYY-MM-DD-short-symptom.md
106
+ loam-reports/NNN-YYYY-MM-DD-short-symptom.md
107
107
  ```
108
108
 
109
- The report records expected versus actual behavior, stable codes, relevant locations, write state,
110
- classification and missing evidence. It replaces secrets with `<redacted>`, prefers repo-relative
109
+ `NNN` is the report's ordinal — at least three digits, zero-padded, one more than the highest
110
+ already in the directory, which
111
+ `loam doctor` reports together with how many reports are open, sent, fixed or superseded — and a
112
+ `Status: open` line in the header is where the report's own state is kept (`doctor` reads it from
113
+ the header block above the first `##` heading, never from a quoted template inside the body). The report records
114
+ expected versus actual behavior, stable codes, relevant locations, write state, classification and
115
+ missing evidence. It replaces secrets with `<redacted>`, prefers repo-relative
111
116
  paths, does not copy source/spec bodies, does not retry a writer merely to reproduce it, and never
112
117
  uploads or submits anything automatically. Review the file before sharing or committing it.
113
118
 
package/README.md CHANGED
@@ -5,13 +5,14 @@
5
5
  ![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)
6
6
  ![node: >=22.22.3](https://img.shields.io/badge/node-%3E%3D22.22.3-brightgreen.svg)
7
7
  [![CI](https://github.com/ybotok/loam/actions/workflows/ci.yml/badge.svg)](https://github.com/ybotok/loam/actions/workflows/ci.yml)
8
- [![npm](https://img.shields.io/npm/v/@ybotok/loam.svg)](https://www.npmjs.com/package/@ybotok/loam)
8
+ [![npm](https://img.shields.io/npm/v/@ybotok/loam/alpha.svg?label=npm%20alpha)](https://www.npmjs.com/package/@ybotok/loam)
9
9
 
10
- > **Pre-release: `0.2.0-alpha.4`**, the third published release of the 0.2 line and the newest published
10
+ > **Pre-release: `0.2.0-alpha.6`**, the fifth published release of the 0.2 line and the newest published
11
11
  > version of [`@ybotok/loam`](https://www.npmjs.com/package/@ybotok/loam). It publishes under the
12
- > `alpha` npm dist-tag: `npm i @ybotok/loam@alpha`, or the exact version, always installs this
13
- > line. `latest` is moved onto a release by hand once it has published, so a plain
14
- > `npm i @ybotok/loam` may still resolve to `0.1.0-beta.3` until it is. See [Status](#status).
12
+ > `alpha` npm dist-tag — which is what the badge above reads: `npm i @ybotok/loam@alpha`, or the
13
+ > exact version, always installs this line. `latest` is moved onto a release by hand once it has
14
+ > published, so a plain `npm i @ybotok/loam` resolves to an earlier release until it is moved. See
15
+ > [Status](#status).
15
16
 
16
17
  `loam` keeps an authored C4 model, requirements, API and event contracts, permissions, domain
17
18
  vocabulary and verification evidence in explicit agreement as a system changes. The same lifecycle
@@ -70,7 +71,8 @@ Topology changes where loam's checks pay off; it does not change the lifecycle:
70
71
  nesting depth, decides which `services/<id>/` owns an element.
71
72
 
72
73
  For example, the frozen `service` term can name an application while its internal modules remain
73
- ordinary C4 elements:
74
+ ordinary C4 elements. This is `architecture/landscape.likec4`, the fleet map — the one document
75
+ that declares the kinds:
74
76
 
75
77
  ```likec4
76
78
  specification {
@@ -91,6 +93,11 @@ model {
91
93
  }
92
94
  ```
93
95
 
96
+ The modules can equally live in `services/commerce-app/model.likec4`, which is the ordinary place
97
+ for them once the application has one — as an **extending model**, declaring no kinds of its own and
98
+ saying `extend commerce { … }` on the element above. SCHEMA.md's "Two shapes of a service model" has
99
+ that shape, the older one that parses alone, and the rule that picks between them.
100
+
94
101
  An internal edge normally carries no `metadata { op }`: that key specifically joins a call to the
95
102
  governed boundary's OpenAPI contract. An architecture requirement can instead say
96
103
  `Covers: commerce.orders -> commerce.billing`. Fleet-level maturity, dependency and scorecard views
@@ -108,7 +115,7 @@ both halves are written down: the question in the brief or the checklist, the an
108
115
 
109
116
  Most of this category will tell you what it checks. loam ships the other list too, in the binary:
110
117
  **[`src/core/brief/unchecked.ts`](https://github.com/ybotok/loam/blob/main/src/core/brief/unchecked.ts)
111
- is sixteen statements of what no check will ever tell you**, printed into the adoption brief beside
118
+ is seventeen statements of what no check will ever tell you**, printed into the adoption brief beside
112
119
  the checks that do run. It says, in loam's own words, that nothing here knows whether the model is
113
120
  the architecture the code actually has, whether a requirement is *true*, whether an `operationId` is
114
121
  the one the code serves, or whether `sources` names the files anybody read. And it says the one that
@@ -358,7 +365,7 @@ cannot hold.
358
365
  | `loam new <FEAT>` | `--title <text>` `--touches <id>` `--new-service <id>` `--capability <id>` | Scaffold a feature: intent, C4 delta, a requirement delta per service named by `--touches`/`--new-service`, and a capability delta per promise named by `--capability` (all repeatable) |
359
366
  | `loam show <service\|FEAT>` | `--type <kind>` | Everything loam knows about one service or feature. A feature's JSON includes a semantic `review` pack: intent, exact architecture objects, API/event slices, dependencies, artifacts, verification, blockers and next actions. `--type` forces an ambiguous reading |
360
367
  | `loam subsystem <verb> [names...]` | `--into <name>` `--under <name>` `--title <text>` `--description <text>` `--owner <name>` | Manage the grouping tree under `services/` — a navigable tree no identity depends on. `new`/`rm`, `move … --into`, `rename`, `list`, `history`, `sync` |
361
- | `loam adopt` | `--service <id>` `--subsystem <name>` `--targets` | Brief an agent to write this service's baseline into the docs repo as `draft`: the target paths, the grammar of each, what the landscape already says, and the checks that follow. Writes nothing. `--targets` narrows it to what varies by service — the walk, the 37 checks and the sixteen statements of what nothing checks are the same bytes everywhere, so a `full` field names the one run that carries them: 42,873 bytes to 17,805 |
368
+ | `loam adopt` | `--service <id>` `--subsystem <name>` `--targets` | Brief an agent to write this service's baseline into the docs repo as `draft`: the target paths, the grammar of each, what the landscape already says — and, for an element no edge touches, the edges the service's own model.likec4 attests — and the checks that follow. Writes nothing. `--targets` narrows it to what varies by service — the walk, the 52 checks and the seventeen statements of what nothing checks are the same bytes everywhere, so a `full` field names the one run that carries them: 54,833 bytes to 20,062 on the example fleet's payment-service (measured 2026-09-04) |
362
369
  | `loam delta <FEAT>` | `--service <id>` | Project a feature onto one service: the intent, its requirement deltas with every body and Given/When/Then line verbatim, the endpoints it adds or retires, and the edges around it. Doubles as a coding-agent task |
363
370
  | `loam gherkin [<FEAT>]` | `--service <id>` `--dry-run` | Emit spec scenarios as digest-stamped Gherkin `.feature` files into the service repo's `<gherkinDir>/loam/` |
364
371
  | `loam rebase [<FEAT>]` | `--service <id>` `--dry-run` `--living` | Pin the feature to the living versions it was written against, on the requirement axis and the contract axis. `--living` takes no feature: it pins the living corpus's `Realizes:` entries to the capability requirements they name, which is what makes `capability.realizes-stale` able to fire later |
@@ -486,7 +493,7 @@ always preserved byte for byte, with the entry printed for manual merge.
486
493
  maps, what is lost, what must be added.
487
494
  - [ROADMAP.md](ROADMAP.md) — the evidence-backed improvement plan, priorities, non-goals and exit
488
495
  criteria.
489
- - [CHANGELOG.md](CHANGELOG.md) — released beta.1–alpha.4 plus the changes on `main` under
496
+ - [CHANGELOG.md](CHANGELOG.md) — released beta.1–alpha.6 plus the changes on `main` under
490
497
  `[Unreleased]`.
491
498
  - [docs/BENCHMARKS.md](https://github.com/ybotok/loam/blob/main/docs/BENCHMARKS.md) — what a
492
499
  fleet-sized run costs, with the method, the machine and the committed runs behind every number.
@@ -520,14 +527,14 @@ component baselines, the `architecture/permissions.yaml` authorization vocabular
520
527
  authored-capability axes with the `Realizes:` join, use cases graded as `dynamic view`s, the
521
528
  `glossary/<term>.md` domain vocabulary checked through the links that cite it, and the
522
529
  `architecture/obligations.yaml` architectural obligations checked through `#obl-` tags and
523
- `Covers:`. Known limits, each with its owner in [ROADMAP.md](ROADMAP.md): the complete gate still
524
- needs repeatable CI and installed-package evidence observed from an actual push. Speculative
525
- `render`, health composition and UI generation come later. Behind that status stand **157 test files** (counted 2026-09-02): the count is graded
530
+ `Covers:`. Known limits, each with its owner in [ROADMAP.md](ROADMAP.md): the scheduled CI
531
+ `stability` job has still to be observed green. Speculative `render`, health composition and UI
532
+ generation come later. Behind that status stand **173 test files** (counted 2026-09-04): the count is graded
526
533
  against a live readdir by `test/docs-facts.test.ts`, so this sentence fails the suite the moment it
527
534
  trails the tree.
528
535
 
529
536
  The package is published as [`@ybotok/loam`](https://www.npmjs.com/package/@ybotok/loam), currently
530
- `0.2.0-alpha.4`: the unscoped `loam` name is taken by an unrelated GDAL wrapper, so the package ships
537
+ `0.2.0-alpha.6`: the unscoped `loam` name is taken by an unrelated GDAL wrapper, so the package ships
531
538
  under the maintainer's own npm user scope. Releases are tag-driven and maintainer-only
532
539
  ([CONTRIBUTING.md](CONTRIBUTING.md)); the version this line names is pinned to `package.json` by a
533
540
  drift test, so it cannot silently trail a release.
package/ROADMAP.md CHANGED
@@ -18,27 +18,30 @@ acceptance tests, and implementation evidence remain ordinary files in repositor
18
18
  state inside a service. The CLI is small enough to audit, and every writer — not only archive — now
19
19
  commits through a locked, journaled transaction that a crash cannot leave half-applied.
20
20
 
21
- The tree contains **427 TypeScript modules in 130 source packages**, with an acyclic package graph
21
+ The tree contains **464 TypeScript modules in 143 source packages**, with an acyclic package graph
22
22
  checked by
23
23
  [scripts/package-graph.mjs](https://github.com/ybotok/loam/blob/main/scripts/package-graph.mjs). The
24
24
  CLI exposes **29 commands** from [src/cli.ts](https://github.com/ybotok/loam/blob/main/src/cli.ts),
25
- and the suite stands at **157 test files**. Those four counts are deliberately stated OUTSIDE the
25
+ and the suite stands at **173 test files**. Those four counts are deliberately stated OUTSIDE the
26
26
  dated snapshot below: each derives from the tree in one readdir, so
27
27
  [test/docs-facts.test.ts](https://github.com/ybotok/loam/blob/main/test/docs-facts.test.ts) grades
28
28
  them live and this paragraph cannot quietly trail the code the way its predecessor did.
29
29
 
30
- _Measured 2026-09-03 at `0.2.0-alpha.4`, and dated because neither number has a cheap derivation:_
30
+ _Measured 2026-09-04 at `0.2.0-alpha.6`, and dated because neither number has a cheap derivation:_
31
31
  lint, typecheck, `npm run arch:check` and `npm run meta:check` green; `npm test` passing
32
- **3,410/3,410 tests**, with two `skipIf(asRoot)` cases the root gate container cannot run; the
33
- coverage gate passing with **92.26% statements, 84.06% branches, 96.62% functions, and 94.20%
32
+ **3,772/3,772 tests**, with two `skipIf(asRoot)` cases the root gate container cannot run; the
33
+ coverage gate passing with **92.78% statements, 84.65% branches, 97.08% functions, and 94.63%
34
34
  lines** against its thresholds of 91 / 82 / 95 / 93; and `npm run release:check` plus
35
35
  `npm run test:package` green against the real tarball.
36
36
 
37
- One qualification remains, and it is observation rather than code: nothing has been pushed since the
38
- CI `stability` job and the tarball-reading package smoke landed, so repeatable CI executions — the
39
- stability job seen green, and the installed-package smoke run from a pushed commit — are still
40
- release evidence to collect, even though the gate-stress proof passed locally on its landing tree
41
- and the smoke passes locally against the real tarball. The runner class recorded on 2026-08-18 is
37
+ Most of that qualification is now closed by observation rather than by code. Measured 2026-09-03 on
38
+ pushed commits: `ci` at `1de7c97` green — `test` on Node 22.22.3 and 24, and the installed-package
39
+ `package` job on ubuntu (both Node lines) and windows — and the `release` workflow on tag
40
+ `v0.2.0-alpha.4` green end to end, `release-gate` (lint, typecheck, coverage, the packed candidate,
41
+ the smoke of that exact candidate, `release:check`) followed by `publish`, which put the tarball on
42
+ npm under the `alpha` dist-tag. What is left is one job: `stability` is SKIPPED on a push — it runs
43
+ on the weekly schedule and on dispatch — so it is still release evidence to collect, even though the
44
+ gate-stress proof passes locally. The runner class recorded on 2026-08-18 is
42
45
  still the reading to apply to a red of that shape: a restricted filesystem sandbox that denies `tsx`
43
46
  its local IPC socket fails only the
44
47
  [test/cli-entry.test.ts](https://github.com/ybotok/loam/blob/main/test/cli-entry.test.ts) cases with
@@ -103,12 +106,15 @@ has landed (see [Recently landed](#recently-landed)) — the authored business a
103
106
  axis, and as of 2026-09-02 the deployment axis, which is the first item this file ever wrote
104
107
  directly into this section rather than promoting from [Later](#later--promote-only-from-evidence).
105
108
 
106
- ### Release evidence, unchanged
109
+ ### Release evidence — one job left
107
110
 
108
- What is left is the one thing this file cannot close by writing code, and it stays
109
- where it is rather than being promoted for the shape of it: the CI `stability` job and the
110
- installed-package smoke have still to be OBSERVED green from a pushed commit. That is release
111
- evidence to collect, not an improvement to make.
111
+ What is left is the one thing this file cannot close by writing code, and it stays where it is
112
+ rather than being promoted for the shape of it. The installed-package half closed on 2026-09-03:
113
+ the `package` job ran green on a pushed commit across three runner/Node combinations, and the whole
114
+ publication path ran green on tag `v0.2.0-alpha.4`. The CI `stability` job has still to be OBSERVED
115
+ green — it is skipped on a push and runs on the weekly schedule — so it is still release evidence to
116
+ collect, not an improvement to make. `workflow_dispatch` is the way to collect it deliberately
117
+ rather than waiting for Monday.
112
118
 
113
119
  An item returns to this section from [Later](#later--promote-only-from-evidence) when fleet evidence
114
120
  names its operator, repeated task, failure mode, frequency, current workaround and measurable
@@ -121,9 +127,270 @@ exactly the invented need the non-goals forbid.
121
127
  Closed since the 2026-08-18 assessment, each with the commit or commit range that landed it on
122
128
  `main`. [CHANGELOG.md](CHANGELOG.md) is the user-facing record; the full item texts — required
123
129
  changes, exit criteria, and what each review surfaced — are in this file's history (`git show
124
- 5cd3942:ROADMAP.md`). The three use-case entries below carry no range yet: they are green in the
125
- working tree and not committed, and the range goes in with the commit rather than being guessed at
126
- here.
130
+ 5cd3942:ROADMAP.md`). The four 2026-09-03 problem-report entries that follow — the extending model
131
+ and the report ordinal, which open the list — landed together in `f751909`, one change built as
132
+ four disjoint workstreams and gated as one; the three before them — the edgeless brief, the
133
+ intra-service use case, and the styling paragraph under the generated views — landed together in
134
+ `0eaa101` the same way.
135
+
136
+ #### One model, many documents — a service model may extend the fleet map
137
+
138
+ **Landed 2026-09-04** in `f751909`, from three problem reports that turned out to be one cause,
139
+ with a fourth landing beside it in the same change. #010 measured the cause: a service model must
140
+ parse alone, so it re-declares its own `specification` and every partner it names — 78 double
141
+ declarations in a 56-service fleet with four services adopted, projected to ~448 at full adoption —
142
+ and the copies had already diverged, `#platform` missing from every copy of the broker and the
143
+ identity provider but the map's, with no finding code for "two documents describe one element
144
+ differently" because no check read two documents together. #009 measured the symptom: 57 flat
145
+ entries in the renderer's project picker, 52 of them a single box, forced by the per-service
146
+ project file `0.2.0-alpha.5` had to write **because** the models could not share a project. #011 is
147
+ the structural proposal, every claim of it executed against copies of `examples/docs`: `extend`
148
+ resolves across documents of one project, a model rewritten that way makes its containers visible
149
+ in the fleet project with one copy of the broker in the whole model, and deleting a model's
150
+ `specification` under the old loader is eleven `c4.invalid` errors — so the change had to be
151
+ loam's, not a convention a repository could adopt. (#012, reports having no ordinal and loam never
152
+ reading `loam-reports/`, landed in the same commit and has its own entry below.)
153
+
154
+ **What landed.** The shape of a model is now read from the file's own grammar — a top-level
155
+ `specification { }` block containing an `element` declaration means STANDALONE, its absence means
156
+ EXTENDING — and each shape gets the grade the other cannot need: `c4.declaration-diverged` for a
157
+ standalone model whose copies of the map's elements disagree with it, `c4.element-unowned` for an
158
+ extending model that declares an element outside its own. Around them, the renderer wiring
159
+ `loam subsystem sync` now owns end to end (`service.likec4-config-stray`, `service.model-excluded`,
160
+ `service.model-unexcluded`, the narrowed `service.likec4-config-missing`, and the root project's
161
+ `exclude` rewritten to cover exactly the models that stand alone), and `c4.fleet-project-invalid`,
162
+ which loads the fleet project the way the renderer builds it and reports what only that load can
163
+ see. `examples/docs` is migrated whole, and its ten deliberate warnings are unchanged. CHANGELOG
164
+ has every code, key and message; SCHEMA's new "Two shapes of a service model" has the rules and the
165
+ migration recipe.
166
+
167
+ **Rejected, each with the reason.** Migrating repositories automatically — the recipe is documented
168
+ and an agent performs it, because a real model's comments are the team's and not loam's to reflow.
169
+ Making the shape a key in `loam.json` or `likec4.config.json` — the file's grammar already decides
170
+ how it can be read, and a key would be a second answer able to disagree with the first. Widening
171
+ loam's fleet map to the renderer's root project — the map is graded as `architecture/` alone so
172
+ that one broken service model cannot blank the fleet's own picture, which is why a cross-service
173
+ flow in `architecture/usecases/` still names service elements and never a container. Retiring the
174
+ standalone shape — it is what lets a service's docs be validated from the service's own repository,
175
+ and a fleet that wants that keeps it, now with a check on the divergence it costs.
176
+
177
+ **The deliberate remainder**, stated so nobody reads it as an oversight. A cross-service call still
178
+ exists TWICE: container-level in the model, service-level in the map (#011's item 6). The spine,
179
+ the platform census and every other fleet check read the map alone, and `landscape.service-isolated`
180
+ remains the nudge that carries an attested call up into it. Thinning the map to elements plus
181
+ cross-service edges is the [Later](#later--promote-only-from-evidence) "Landscape scaling" row, and
182
+ it is what would remove the duplication. Beside it sits #011's item 5, also deliberate: a
183
+ cross-service flow cannot name a container, because loam grades the fleet map as its own document
184
+ set. Revisit that one when a fleet asks for a cross-service flow whose hops are containers — the
185
+ renderer already resolves such a hop, so the ask will be concrete when it arrives.
186
+
187
+ **Verified 2026-09-04**, and three things the verification changed. The report's own "next thing to
188
+ check" was the one that did not compose: `loam archive` spliced a delta's nested additions into the
189
+ fleet map, and the extending model could then never declare them — the service went `c4.invalid`
190
+ (duplicate) the moment it wrote the container the adopt brief prescribed. The merge now ROUTES each
191
+ addition to the document that owns the interior, and proves the merged model parses beside the
192
+ merged map before writing either. Second, the consumer census read `architecture/` alone, so under
193
+ this shape a private store's consuming edges — which live in the models now — counted for nothing:
194
+ `landscape.datastore-private` went silent or named the wrong service, and a nested store two
195
+ services reach was graded nowhere. It reads every extending model's attested calls now, and
196
+ `landscape.datastore-shared` fires for a nested store. Third, the wiring guard this entry landed
197
+ failed open on every root `exclude` spelling except `services/<tree>/**`, `loam subsystem sync`
198
+ never removed the stray per-service project file it told repositories to delete (and that file
199
+ CLAIMS the model, rather than holding nothing as the design note said), and
200
+ `c4.fleet-project-invalid` enumerated three roots rather than the renderer's — a `.likec4` in a
201
+ subsystem directory was in none of them. All three are closed; CHANGELOG's `[Unreleased]` has the
202
+ codes and keys.
203
+
204
+ **The `import` question is answered.** Reports #010 and #011 left open whether LikeC4's
205
+ cross-project `import` could have delivered this without merging the projects. Measured at the
206
+ 1.59.2 pin: an import resolves REFERENCES across projects, and that is all — it carries no
207
+ `specification`, so a kind or tag the other project declares is not usable in this one, and it does
208
+ not support `extend`. A model importing the map could name the map's elements and still not add a
209
+ container to one. So the choice was never between two mechanisms; one project holding both
210
+ documents is what the shape requires.
211
+
212
+ **Known and carried, 2026-09-04.** The second verification round reproduced these and they were
213
+ NOT fixed. Each is written down with the repro that finds it again, so nobody spends a second
214
+ afternoon rediscovering one. They are carried, not closed.
215
+
216
+ - **Only the basename `likec4.config.json` is surveyed.** LikeC4 1.59.2 also accepts `.likec4rc` and
217
+ six other spellings; drop `{"name":"svc-a"}` into `services/svc-a/.likec4rc` on a synced extending
218
+ fleet and `validate --all`, `doctor` and `subsystem sync` all stay green while the renderer loads
219
+ three projects and refuses without `--project`. Carried on scope: the survey, the stray grade and
220
+ `sync`'s delete list are one table, and widening it is the same change as the two rows below —
221
+ worth doing once, together, with the pin's whole name list measured rather than guessed.
222
+ - **A `likec4.config.json` in a SUBSYSTEM directory claims every model beneath it.** Write one at
223
+ `services/platform/likec4.config.json` over three extending models and all three go
224
+ `Could not resolve reference to Element named 'svc_svc_d'` in the renderer, while loam names
225
+ nothing (`sync` → "nothing to write"). Carried on scope, with the row above: the checks look one
226
+ directory deep, at a service, and seeing this needs a walk of the whole docs tree.
227
+ - **`sync` keeps a config beside a model that is GONE, and that leftover project is not harmless.**
228
+ `rm services/svc-c/model.likec4` with its config left behind: loam is green everywhere, and
229
+ `likec4 validate` — and `likec4 build` — at the docs root then need `--project`. The keep is
230
+ deliberate ("that one really is the team's"), but its premise, that an empty nested project is
231
+ invisible to the renderer, is false at the pin. Carried on a design decision pending: deleting a
232
+ file loam never wrote needs a rule about whose file it is, not a bug fix.
233
+ - **`validate --feature` does not preview a merge refusal.** A delta drawing a cross-interior edge,
234
+ or naming a kind the map does not declare, validates clean and then refuses at `archive`
235
+ (`merge-failed`, nothing written). Carried on scope: the preview needs the archive's own merge
236
+ planner run from the validate path, which is a new seam rather than a message.
237
+ - **In the STANDALONE shape a broken UNTAGGED sibling is invisible to loam.** Make one service
238
+ standalone, `subsystem sync`, then write an unresolvable `services/<id>/usecases/plain.likec4`:
239
+ loam prints `c4.valid` and 0 errors while `likec4 validate --project <id> .` is Invalid. The
240
+ extending shape catches the same file through `c4.fleet-project-invalid`. Carried on scope: the
241
+ per-service load reads the model, and reading the standalone project the renderer builds is a
242
+ second loader.
243
+ - **A root `exclude` that no longer covers `features/**` is silent.** Delete that one entry from
244
+ `examples/docs`' config: loam reports 0 errors and `sync` says "nothing to write", while the
245
+ renderer's fleet project is at 346 errors. The scaffold always writes the entry, so this is a
246
+ hand edit, which is why it is carried rather than fixed — on the evidence bar: no repository has
247
+ hit it, and a grade on an entry loam did not write is a policy claim.
248
+ - **A root config the renderer REJECTS is reported only by `sync`'s note.** `"exclude": "services/**"`
249
+ as a string, or an array-valued `include`, is "loaded 0 projects out of 1" — nothing renders — and
250
+ `validate --all` is green; an unparseable file is the same. Carried on scope: loam reads the file
251
+ for one key, and schema-validating somebody else's config is a dependency on the renderer's own
252
+ schema at the pin.
253
+ - **`LivingSlice.landscape.modelled` is `false` where `adopt` says `null`.** Over an
254
+ `architecture/` project that did not parse, `loam context <svc> --json` says `modelled: false` and
255
+ `loam adopt --service <svc> --json` says `modelled: null` about the same tree. Carried on scope:
256
+ the field is typed non-nullable in the pack, so agreeing with `adopt` is a contract change.
257
+ - **`loam show` never mentions graded intra-service flows.** With a healthy `#req-…`-tagged flow
258
+ beside an extending model — `validate --service` prints "· 1 tagged flow(s) graded" — neither
259
+ `loam show <svc>` nor its `--json` names it anywhere. Carried on scope: `show`'s payload is a
260
+ frozen shape and the flow needs a section of its own.
261
+ - **`loam status` never names the isolated state.** `landscape.service-isolated` is a warning
262
+ `validate --all` prints and `status`'s next-action ladder has no rung for. Carried on a design
263
+ decision pending: the ladder is derived from artifact presence and provenance, and a finding-derived
264
+ rung is a different rule for what `next[0]` means.
265
+ - **The first ever container-level flow a FEATURE brings.** A cross-service flow cannot name a
266
+ container, because loam grades the fleet map as its own document set; the renderer already resolves
267
+ such a hop. Carried deliberately — the [Later](#later--promote-only-from-evidence) "Landscape
268
+ scaling" row owns it, and it is promoted when a fleet asks, not before.
269
+
270
+ #### Problem reports carry an ordinal, and loam reads the directory it prescribes
271
+
272
+ **Landed 2026-09-04**, from one problem report (#012). The `loam-report` protocol prescribed a
273
+ directory, a filename shape and a document structure and then stopped: files were named
274
+ `YYYY-MM-DD-<slug>.md` with a numeric suffix only on collision — a collision breaker, not an
275
+ identifier — so in a directory of eleven reports written in two days, none had one, four shared
276
+ their first eleven characters, and the discriminating part of every name sat far to the right of
277
+ what a terminal, a file picker or a chat message shows. There was no field anywhere in the document
278
+ for the one piece of state a report has (three of the eleven were already closed and one superseded,
279
+ all of it living in whoever remembered), and `validate --all`, `doctor`, `status` and `list`
280
+ mentioned `loam-reports` zero times between them. Landed: an unconditional ordinal at the front of
281
+ the name, which is the computation the protocol already described for its suffix applied where it is
282
+ visible; a `Status:` line in the template with a four-value vocabulary a report closes by editing;
283
+ and `loam doctor` reading the directory — total, per-status counts and the next ordinal — as an
284
+ additive `reports` key and one human row. Rejected: a `loam report list` verb (a new command for a
285
+ count `doctor` was already the place for, on a frozen CLI surface); an index file (the workaround
286
+ this replaces — a second place to keep in step, and the reason the state left the files in the first
287
+ place); grading the corpus (an open report is not a defect in the repository holding it, so `doctor`
288
+ reports it as state and `healthy` never moves); parsing past the first word of the status line (the
289
+ rest is prose an author writes for a reader). Cost: one `readdir` and one small read per report on
290
+ every `loam doctor`, and nothing is transmitted — which is the constraint the protocol is built
291
+ around and the reason the whole fix needed no new state.
292
+
293
+ #### The adopt brief on an edgeless landscape element
294
+
295
+ **Landed 2026-09-03**, from one problem report. `loam adopt` decided whether the fleet map still
296
+ owed a service from element EXISTENCE: a bound element with no edge — what
297
+ `loam seed --from fleet.yaml` leaves for every service absent from `calls:` — read as
298
+ `landscape.instruction: null`, no target, `inbound: []`, `outbound: []` in one payload, so an agent
299
+ following the protocol to the letter wrote a validating baseline and never drew one edge, and
300
+ `loam validate --all` stayed green over it. Landed: a fourth landscape state carried by two
301
+ additive keys (`landscape.touched`, and `landscape.attested` — the calls the service's own
302
+ model.likec4 declares across its boundary, derived once in `core/c4/resolve/attested.ts` for the
303
+ brief and the fleet check alike, so the two can never name different edges); a non-null instruction
304
+ in two arms (carry each attested call up as one edge on the existing element, naming the other
305
+ party as the map spells it; or, with nothing attested, draw NOTHING rather than invent — an edgeless
306
+ element that is true is a fact); the landscape target back as an edge-only `edit` whose shape
307
+ refuses a second element; the `EDGELESS` flag in the text view; the evidence-gated
308
+ `landscape.service-isolated` warning under `--all`; and a seventeenth unchecked statement about
309
+ visibility (placeholder tags, curated views, `include *` over a boundary). Rejected: folding the
310
+ state into `landscape.modelled` (an agent told `false` draws a second box); an unconditional "no
311
+ edge touches it" warning (no correct fix for a worker that calls nothing, and every seeded fleet
312
+ red-ish until adoption); grading the SET DIFFERENCE between the attested calls and the drawn edges
313
+ (loam cannot tell a call the map forgot from one the fleet does not draw at its own altitude, so
314
+ the difference reported as an omission is an invented edge with a code beside it — the check stays
315
+ touched/untouched, and the brief says so out loud); a check on placeholder tags or on what a view
316
+ shows (rule 26 — loam never
317
+ computes what a view shows). Cost: one LikeC4 load of `model.likec4`, only in the edgeless state, a
318
+ memo hit under `--all`.
319
+
320
+ **Corrected 2026-09-04** by the verification of this entry: the brief read
321
+ `architecture/landscape.likec4` alone where the fleet check reads the whole `architecture/` project,
322
+ so a fleet keeping a second `model { }` block in a sibling got a brief demanding edges it had
323
+ already drawn while `validate --all` reported `landscape.matched`. Both now read the project.
324
+
325
+ #### A home for an intra-service use case
326
+
327
+ **Landed 2026-09-03.** Measured: `architecture/usecases/` cannot resolve a container (one such file
328
+ is `landscape.invalid` plus a `spine.landscape-invalid` per service), and a view beside the model
329
+ rendered while nothing graded it. Landed: the service's own LikeC4 project as the slot — every
330
+ `.likec4` beside model.likec4 and any tagged view inside it — graded by `validate --service`/`--all`
331
+ with the five existing `usecase.*` codes, `#req-` scoped by the service's own Requirement-IDs,
332
+ `#cap-` refused by placement, a broken sibling one `usecase.flow-invalid` while the model keeps
333
+ grading; plus the tier-2 skip and the fourth `step-unlinked` guard in the shared graders. Rejected:
334
+ a fixed `usecases/` directory alone (the reporter's `views.likec4` would render ungraded),
335
+ `views.likec4` alone (a second file would be invisible), a new code for the broken sibling (nothing
336
+ branches differently), gating the read on `likec4.config.json` (create-only, deletable). Cost: one
337
+ recursive readdir per service; a workspace only where a view in model.likec4 opts in or a sibling
338
+ mentions a reserved prefix anywhere in its bytes — an untagged sibling is never graded, and once
339
+ the workspace is loaded a project that does not read is one `usecase.flow-invalid` whether or not a
340
+ view opted in.
341
+
342
+ #### The service model, renderable from the fleet root
343
+
344
+ **Landed 2026-09-03**, from one problem report. The root `likec4.config.json` that
345
+ `loam init --create` writes excludes `services/**` on purpose — every `model.likec4` declares its
346
+ own `specification` block, and a renderer merging the tree calls each one a duplicate — and the
347
+ cost of that exclusion had gone unstated for a release: the model `loam adopt` spends the most
348
+ effort on was a box with nothing inside it in the renderer opened at the docs root, and the only
349
+ documented path to its containers was one renderer invocation per service directory. The report's
350
+ hand-written workaround, one project file per service, is what this entry makes loam's.
351
+
352
+ **Measured** at the pinned likec4 before a line was written. A `services/<svc>/likec4.config.json`
353
+ is registered as a project of its own even though the root excludes the directory: the workspace
354
+ loads `fleet` plus one project per service with zero errors, the root project sees exactly the
355
+ files it saw before, and each service project carries an auto `index` view of the containers beside
356
+ the authored view. A nested project named `fleet` is silently registered as `fleet-1`. With more
357
+ than one project, `likec4 validate` at the root refuses with `Specify exact project` until given
358
+ `--project`. A nested project holding no `.likec4` file is silently absent from the project list. A
359
+ service model that does not parse breaks only its own project; siblings and `fleet` still compute.
360
+ And the project picker shows `title ?? id`, so the title is what a human reads.
361
+
362
+ **What landed.** `loam subsystem sync` writes the file — create-only, two keys, bytes a function of
363
+ the id alone — beside every service model that has none, once the root project file exists, and
364
+ reports them under an additive `projects` key. `loam validate --all` grades presence with one
365
+ warning per service, `service.likec4-config-missing`, whose fix is `sync`. No new command: the
366
+ [Later](#later--promote-only-from-evidence) Rendering row's evidence bar was met for the SCOPING
367
+ half of its trigger and answered by wiring — one file that tells the renderer where a project is —
368
+ not by a drawing verb; drawing stays LikeC4's, and the row says so now.
369
+
370
+ **Rejected.** A new command, because the report's problem was a missing file, not a missing verb,
371
+ and a verb would have put loam between a team and its renderer for good. The agent writing the file
372
+ from the adopt brief, because a file written by hand per service is the state the report found —
373
+ one of four adopted services had one — and loam already writes the root file and knows every
374
+ service directory. A generated-and-compared file with a `-stale` grade, because the bytes cannot go
375
+ stale and a compare would fight the `title` and styles LikeC4 lets a team add. Grading in `doctor`
376
+ alone, because the gap is per service and `doctor` reports on the repository, not on its services;
377
+ the docs repo's CI runs `validate --all`.
378
+
379
+ **Costs**, each where a user meets it. A repo with adopted services reports one warning per service
380
+ on its first `validate --all` after upgrading, until one `sync`; that `sync` leaves N untracked
381
+ files it does not `git add`; from the first service project on, `likec4 validate` at the docs root
382
+ needs `--project <name>` (`build` and `export` take every project without one — measured); the
383
+ renderer's project list grows by one per adopted service, and a fleet-map box is not a link into
384
+ that project (LikeC4 projects are separate models); and the project name is not injective (an id spelled billing.core beside one spelled
385
+ billing-core, `service-fleet` beside `fleet`), disclosed in SCHEMA rather than escaped away,
386
+ because the picker shows the id.
387
+
388
+ **Superseded for extending models, one day later.** The last of those costs — one picker entry per
389
+ adopted service — is what report #009 measured at 57 entries, and the entry that opens this list is
390
+ what removed its cause rather than its symptom: a model that extends the fleet map shares the root
391
+ project with it, so it is owed no project file and adds no entry, and `loam subsystem sync` writes
392
+ the file only for a model that stands alone. Everything measured here still holds for that shape,
393
+ and is why it is still supported; it is no longer what a new model is written as.
127
394
 
128
395
  #### The generated subsystem views — a label, a boundary, and one reader of the map
129
396
 
@@ -145,6 +412,20 @@ use-case document binding one element could produce a `subsystem.views-stale` th
145
412
  `loam subsystem sync` reported as `current` — reproduced on `examples/docs`, and fixed by pointing
146
413
  both sides at the same read.
147
414
 
415
+ **And the repository's palette (2026-09-03).** A generated view can carry no inline `style` rule —
416
+ the file is regenerated wholesale — and the one mechanism LikeC4 gives a view for borrowing a
417
+ repository's styling is a reference to a global style by id, which is a parse error that blanks the
418
+ whole `architecture/` project when nothing declares it. So the convention is one reserved name:
419
+ declare `global { styleGroup subsystems { … } }` anywhere in the `architecture/` project and every
420
+ generated view references it; declare nothing and the file is what it was. Rejected: reading the id
421
+ off a key in `likec4.config.json` (create-only, never re-read, and not a LikeC4 document whose
422
+ contents a parse can check), and "reference the single declared group" (a second group declared for
423
+ a hand-drawn view would silently switch the generated views' styling off and restale the file on a
424
+ change that touched no subsystem). The presence read is a census of the keys of
425
+ `$data.globals.styles`, a declaration read like `specification`; seven tripwire assertions in
426
+ `test/likec4-view-shape.test.ts` pin the shape so a likec4 bump fails there before it reaches a
427
+ generated file.
428
+
148
429
  #### The deployment axis — topology joined to requirements
149
430
 
150
431
  **Promoted 2026-09-02, and authored as a feature in loam's own docs repo** rather than as an item
@@ -365,7 +646,9 @@ item gets promoted — it went from here to `## Now` and from there to
365
646
  invocation, scoping, or artifact reproducibility is the recurring problem, and it must stay
366
647
  outside validation so view computation cannot slow or change the gate. This item is about
367
648
  **drawing**, and LikeC4 keeps that job. Reading a `dynamic view`'s declared steps is not rendering
368
- and is not this item — see docs/DESIGN.md rule 26.
649
+ and is not this item — see docs/DESIGN.md rule 26. The scoping half of that bar was met and
650
+ answered on 2026-09-03 by the per-service project file `loam subsystem sync` writes (see
651
+ [Recently landed](#recently-landed)) — wiring rather than a verb; drawing stays LikeC4's.
369
652
  - **UI generation:** begin with a disposable projection over the stable JSON contract only after CLI
370
653
  consumers demonstrate a repeated navigation problem. It must not introduce a second mutable state,
371
654
  hidden workflow state, or a required service.
@@ -381,9 +664,33 @@ item gets promoted — it went from here to `## Now` and from there to
381
664
  for every fleet that never writes one. Promote only on evidence from a second fleet, after living
382
665
  with the disclosure — the first report classified itself `inconclusive`, and one fleet is not
383
666
  enough to qualify an invariant that says placement is never part of any identity.
384
- - **Landscape scaling:** retain one landscape while conflicts are exceptional. If same-service
385
- conflicts become routine — the current trigger in [SCHEMA.md](SCHEMA.md) is weekly rather than
386
- monthly — evaluate service-owned model files plus a thin global cross-service map. Migration must
667
+ - **A feature brings an intra-service flow.** `features/<FEAT>/specs/<svc>/usecases/<name>.likec4`,
668
+ copied create-only by `loam archive` into `services/<…>/<svc>/usecases/` and taken back by
669
+ `loam unarchive`, graded before the copy against the service's own LikeC4 project with the
670
+ feature's `specs/<svc>/` requirement deltas in `#req-` scope — `usecase.flow-exists` and
671
+ `usecase.flow-invalid` reused, never `--approve`-overridable. Deliberately NOT built with the
672
+ living slot (2026-09-03): the version-3 snapshot manifest already carries an artifact path with a
673
+ directory in it, so nothing about the undo changes when it lands, and no model delta exists to
674
+ preview — what it waits for is one fleet asking for a container-level flow to ship in the same
675
+ change as the `arch.spec.md` requirement it answers. Until then a service-local flow is written
676
+ directly into the service directory in the same pull request, where git produces the conflict;
677
+ building the feature route ahead of that evidence is the invented need the non-goals forbid.
678
+ - **Landscape scaling:** retain one landscape while conflicts are exceptional, and half of what
679
+ this row once asked for arrived on 2026-09-04. Per-service internals now live in
680
+ `services/<svc>/model.likec4` by default: `loam archive` routes a delta's fqn-spelled interior
681
+ into the owning service's extending model, and a team adopting a service adds nothing to
682
+ `architecture/landscape.likec4` but one element and its cross-service edges. The map holds a
683
+ service's interior only where a team puts it on purpose — a store nested under its owner so a
684
+ second service can name it (SCHEMA's "Private stores") — or where a delta spells its parent by
685
+ title rather than by the map's id (the archive's documented remainder). What is
686
+ left is the CROSS-SERVICE EDGE, which still exists twice — container-level in the model,
687
+ service-level in the map — and that duplication is **deliberate**, not a leftover: the spine, the
688
+ platform census, `landscape.service-isolated` and every other fleet check read the map alone,
689
+ which is what lets a fleet answer "who calls this service" from one diffable document without
690
+ parsing sixty. Thinning the map to elements plus cross-service edges means teaching those checks
691
+ to read the edges out of the models that attest them, and paying the blast radius that comes with
692
+ it. If same-service conflicts become routine — the current trigger in [SCHEMA.md](SCHEMA.md) is
693
+ weekly rather than monthly — evaluate that. Migration must
387
694
  preserve archive/undo, deterministic resolution, and readable plain files.
388
695
  - **Authored business axis — PROMOTED 2026-08-27, and landed in full; see
389
696
  [Recently landed](#recently-landed).** The gate asked for evidence of authorship: analyst edits