@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
@@ -1 +1 @@
1
- {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../src/commands/seed/plan.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What a seed run will write, decided in full before a byte lands. Split from\n * `./commit.ts` on the phase seam: that module owns the WINDOW — the lock, the\n * interrupted-commit recovery, the two refusals and the transaction — while\n * this one answers the single question \"given the tree as it is and the fleet\n * file as it reads, which files change and how\". Nothing here acquires\n * anything, and nothing here writes: it is called under the lock and hands\n * back a plan the transaction executes or rolls back whole.\n */\nimport { existsSync } from \"node:fs\";\nimport { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { Elem } from \"../../core/c4/likec4.js\";\nimport type { FleetSeed } from \"../../core/c4/seed/fleet-file.js\";\nimport { serviceDirOf, type DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport {\n landscapePath,\n servicePathsUnder,\n servicesDir,\n subsystemPathUnder,\n subsystemViewsPath,\n unfiledServicePaths,\n} from \"../../core/repo/paths.js\";\nimport { SUBSYSTEM_MARKER } from \"../../core/repo/tree/marker.js\";\nimport { renderSubsystemViews, viewsAgree } from \"../../core/repo/tree/render/views.js\";\nimport type { FleetTree, SubsystemEntry, WalkedService } from \"../../core/repo/tree/walk.js\";\nimport { TEMP_FILE_RE } from \"../../core/staging/commit.js\";\nimport { planWrite, type PlannedWrite } from \"../../core/staging/writes.js\";\n\n/** Everything the plan reads. The commit request extends this and adds only `rerun`. */\nexport interface SeedPlanInput {\n docsDir: DocsDir;\n seed: FleetSeed;\n /** The sealed landscape text (stamp on line 1), already proven to parse. */\n landscape: string;\n /**\n * That same text's parsed elements — the loadSource self-check's output,\n * reused for the views join so the generated views file agrees byte for byte\n * with what `validate --all` will recompute from the committed landscape.\n */\n elements: Elem[];\n}\n\nexport interface SeedPlan {\n writes: PlannedWrite[];\n /** Service ids split by whether this run creates the directory. */\n services: { created: string[]; existing: string[] };\n}\n\n/**\n * Does this existing service directory hold nothing git would keep? An empty\n * directory is a service to the walk but nothing at all to git, so a landscape\n * declaring it clones into a repo where `validate --all` fails on a service\n * that exists only on the author's disk. Seed's own crash window produces\n * exactly this tree — `stageWrites` creates directories before the journal is\n * written, so a kill in between leaves directories no recovery knows about —\n * and so does a plain `mkdir`. Staging temps do not count as content: they are\n * that same crash's litter, not somebody's file.\n */\nasync function keepsNothing(dir: string): Promise<boolean> {\n try {\n return (await readdir(dir)).every((entry) => TEMP_FILE_RE.test(entry));\n } catch {\n // Unreadable is not empty: the walk found this directory a moment ago, and\n // inventing a write into a directory we cannot list is the wrong guess.\n return false;\n }\n}\n\n/**\n * The complete plan, in memory, before a byte lands: the sealed landscape, one\n * marker per NEW subsystem, one `.gitkeep` per service directory this commit\n * creates — and per existing one holding nothing git would keep (see\n * `keepsNothing`) — and the generated views file rendered from the tree AS\n * THIS COMMIT LEAVES IT, never from a re-walk between two states\n * (commands/subsystem/txn/views.ts's `movedTree` doctrine). That last one is\n * the only write that can be a DELETE: a tree with no subsystems owes no views\n * file, and a leftover is a finding.\n *\n * Existing directories are never moved: fleet.yaml's placement is ignored for\n * a service that already exists — moving is `loam subsystem move <id> --into\n * <name>`'s job, under its own guards — and nothing anywhere is deleted except\n * that one generated file.\n */\nexport async function planSeedWrites(req: SeedPlanInput, tree: FleetTree): Promise<SeedPlan> {\n const { docsDir, seed } = req;\n const writes: PlannedWrite[] = [planWrite(landscapePath(docsDir), req.landscape)];\n\n const existingSubs = new Map(tree.subsystems.map((s) => [s.name, s]));\n const newSubs: SubsystemEntry[] = [];\n for (const sub of seed.subsystems) {\n if (existingSubs.has(sub as string)) continue;\n const dir = subsystemPathUnder(servicesDir(docsDir), sub);\n // A comment line parses to null — a valid empty marker (tree/marker.ts).\n writes.push(planWrite(join(dir, SUBSYSTEM_MARKER), \"# subsystem marker\\n\"));\n newSubs.push({ name: sub as string, path: [sub as string], dir, meta: {} });\n }\n\n const treeDirs = new Map(tree.services.map((s) => [s.id as string, s.dir]));\n // Every existing directory inspected at once — independent reads, so the\n // loop below stays synchronous.\n const invisible = new Map(\n await Promise.all([...treeDirs].map(async ([id, dir]) => [id, await keepsNothing(dir)] as const)),\n );\n const created: string[] = [];\n const existing: string[] = [];\n const newWalked: WalkedService[] = [];\n for (const s of seed.services) {\n const already = treeDirs.get(s.id as string);\n if (already !== undefined) {\n existing.push(s.id);\n // Existing, but git-invisible: give it the `.gitkeep` a created service\n // gets, or the landscape about to be written declares a directory the\n // next clone will not have. `planWrite` makes this a no-op when the file\n // is already there, so a real service is never touched.\n if (invisible.get(s.id as string) === true) writes.push(planWrite(join(already, \".gitkeep\"), \"\"));\n continue;\n }\n created.push(s.id);\n const parent = s.subsystem === null ? null : existingSubs.get(s.subsystem as string) ?? null;\n const dir =\n s.subsystem === null\n ? unfiledServicePaths(docsDir, s.id).dir\n : parent !== null\n ? servicePathsUnder(parent.dir, s.id).dir\n : // A subsystem this same commit creates: the creation spelling,\n // through the same branded joins the subsystem verbs use.\n serviceDirOf(join(subsystemPathUnder(servicesDir(docsDir), s.subsystem), s.id));\n writes.push(planWrite(join(dir, \".gitkeep\"), \"\"));\n newWalked.push({\n // A ServiceId IS a RawServiceId (the checked subtype), so no re-cast.\n id: s.id,\n dir,\n subsystem: s.subsystem === null ? [] : parent !== null ? parent.path : [s.subsystem as string],\n });\n }\n\n const plannedTree: FleetTree = {\n findings: [],\n services: [...tree.services, ...newWalked],\n subsystems: [...tree.subsystems, ...newSubs],\n };\n // The generated views file, including its ABSENCE. `renderSubsystemViews`\n // returning null means the tree has no subsystems, and its contract is that\n // the file must then not exist — `validate` grades a leftover as\n // `subsystem.views-stale`, so a seed that ignored the file would exit 0 and\n // send the caller, via its own `next` list, into a failing gate over a file\n // it had in hand. `content: null` is the delete the transaction understands\n // (commands/subsystem/txn/txn.ts's `\"removed\"`), and a file that already\n // SAYS this is left out of the journal entirely rather than written back\n // over itself.\n //\n // \"Already says this\" is content, not bytes: `viewsAgree` performs the\n // comparison and records the Windows reason for it, beside the generator\n // that mints the bytes. What gets WRITTEN is unchanged — the LF render, as a\n // Buffer — so this decode is a comparison only, never the string round trip\n // to disk that writes.ts forbids; a views file whose bytes are not UTF-8\n // therefore fails the compare and is regenerated, which is the right answer\n // for it anyway.\n const viewsPath = subsystemViewsPath(docsDir);\n const views = renderSubsystemViews(plannedTree, req.elements);\n const wanted = views === null ? null : Buffer.from(views, \"utf8\");\n const onDisk = existsSync(viewsPath) ? await readFile(viewsPath, \"utf8\") : null;\n if (!viewsAgree(onDisk, views)) writes.push({ path: viewsPath, content: wanted });\n\n return { writes, services: { created: created.sort(), existing: existing.sort() } };\n}\n"]}
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../src/commands/seed/plan.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What a seed run will write, decided in full before a byte lands. Split from\n * `./commit.ts` on the phase seam: that module owns the WINDOW — the lock, the\n * interrupted-commit recovery, the refusals and the transaction — while\n * this one answers the single question \"given the tree as it is and the fleet\n * file as it reads, which files change and how\". Nothing here acquires\n * anything, and nothing here writes: it is called under the lock and hands\n * back a plan the transaction executes or rolls back whole.\n */\nimport { copyFile, mkdir, mkdtemp, readdir, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join, relative, resolve } from \"node:path\";\nimport { architectureDir, architectureProjectDocuments } from \"../../core/c4/project/architecture.js\";\nimport { loadProject } from \"../../core/c4/project/load.js\";\nimport type { FleetSeed } from \"../../core/c4/seed/fleet-file.js\";\nimport { repoPath } from \"../../core/envelope/json.js\";\nimport { serviceDirOf, type DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport {\n landscapePath,\n servicePathsUnder,\n servicesDir,\n serviceViewsPath,\n subsystemPathUnder,\n subsystemViewsPath,\n unfiledServicePaths,\n} from \"../../core/repo/paths.js\";\nimport { SUBSYSTEM_MARKER } from \"../../core/repo/tree/marker.js\";\nimport { readGeneratedFile } from \"../../core/repo/tree/render/service/header.js\";\nimport {\n type MapFacts,\n renderServiceOverviews,\n renderSubsystemViews,\n viewsAgree,\n} from \"../../core/repo/tree/render/views.js\";\nimport type { FleetTree, SubsystemEntry, WalkedService } from \"../../core/repo/tree/walk.js\";\nimport { TEMP_FILE_RE } from \"../../core/staging/commit.js\";\nimport { planWrite, type PlannedWrite } from \"../../core/staging/writes.js\";\n\n/** Everything the plan reads. The commit request extends this and adds only `rerun`. */\nexport interface SeedPlanInput {\n docsDir: DocsDir;\n seed: FleetSeed;\n /** The sealed landscape text (stamp on line 1), already proven to parse. */\n landscape: string;\n /**\n * That same text's parsed facts — the loadSource self-check's output. They\n * were first reused for the views join on their own, on the reasoning that\n * the seed template declares no `global` block (`core/c4/seed/template.ts`)\n * and so the render's style question had the answer \"none\"; true of the\n * template and beside the point, because `validate --all` recomputes the\n * expected bytes from the whole `architecture/` PROJECT, and a palette in a\n * sibling document is in that census (`projectFacts` below carries the\n * defect). They are now the render's input only when the landscape IS the\n * whole project — no sibling document exists — and the fallback when the\n * project does not parse.\n */\n map: MapFacts;\n}\n\n/**\n * The facts the generated views are rendered from: the `architecture/` PROJECT\n * as this commit leaves it — the sealed landscape standing in for the file it is\n * about to become, beside every other `.likec4` the directory already holds —\n * and never the sealed landscape alone.\n *\n * WHY THE PROJECT. `subsystem.views-stale` (`validate --all`) and `loam\n * subsystem sync` both recompute the expected bytes from `loadArchitecture` —\n * the landscape merged with every other `architecture/**` document, the\n * generated file excluded — and the render asks that document set which global\n * style ids it declares. A repository's `global { styleGroup subsystems { … } }`\n * naturally lives in a sibling such as `architecture/usecases/palette.likec4`,\n * since seed regenerates the landscape wholesale, so it was in the grader's\n * census and not in seed's: a seed into such a repo wrote a views file the very\n * next `validate --all` — seed's own first `next` command — graded stale, and\n * re-seeding wrote the same bytes again, the one loop no `next` could leave.\n * Reading the same documents the grader reads is what makes the shared render\n * shared in fact rather than in name (commands/subsystem/txn/views.ts's banner).\n *\n * NO SIBLING, NO WORKSPACE: the landscape's own facts, already in hand from the\n * self-check, ARE the project's, and the common case pays one readdir.\n *\n * STAGED, for `core/c4/project/staged.ts`'s reason: `loadProject` copies\n * documents from disk keyed on their path relative to a base, and the landscape\n * here exists only in memory. That module's staging is bound to a feature's\n * merge preview, so the same handful of copies is made here once more — the\n * temp tree is gone before this returns, and nothing under the docs repo is\n * touched.\n *\n * FAIL CLOSED to the landscape's own facts. A project that does not parse (a\n * sibling carrying an error of its own, or a copy the temp directory refused)\n * declares nothing loam can rely on, so the render carries exactly what it\n * carried before the project was asked, and the seed still lands: that sibling\n * is `landscape.invalid`'s to name on the next `validate --all`, and a seed\n * refusing over it would make seed the first command to grade a file it does\n * not own. Nothing is hidden by the fallback: the grader compares the views\n * file only once the project has parsed (`validate/fleet/landscape.ts`), so\n * the repo sees the one finding that names the sibling, never a stale-views\n * finding over a file no command could have rendered differently.\n */\nasync function projectFacts(req: SeedPlanInput): Promise<MapFacts> {\n const arch = architectureDir(req.docsDir);\n const landscape = resolve(landscapePath(req.docsDir));\n // The PROJECT's documents, root `exclude` applied — `loadArchitecture`'s own\n // list minus the landscape, which is staged from memory just below. Walking\n // `architecture/` directly skipped that filter, and here that is a WRONG\n // answer rather than an over-scan: a `palette.likec4` under an excluded path\n // is a document the renderer never loads, so its `global styleGroup` id put\n // `global style subsystems` into the generated views file — a name the fleet\n // project cannot resolve — and the very next `validate --all` (seed's own\n // `next` command) graded that file against the same set THIS one now reads.\n // The W5b defect, one command over.\n const siblings = (await architectureProjectDocuments(req.docsDir)).filter((p) => resolve(p) !== landscape);\n if (siblings.length === 0) return req.map;\n // The temp directory is made INSIDE the try, not one line above it: a full\n // disk, a read-only temp root, or a TMPDIR naming a directory that no longer\n // exists rejects `mkdtemp`, and outside the try that rejection left `seed` as\n // the generic `internal` refusal on a repository where nothing is wrong — the\n // defect docs/CODE-STYLE.md's \"read inside the try that handles the read\" rule\n // was written from.\n let root: string | null = null;\n try {\n root = await mkdtemp(join(tmpdir(), \"loam-seed-\"));\n const staged: string[] = [];\n for (const path of [landscape, ...siblings]) {\n const dest = join(root, relative(arch, path));\n await mkdir(dirname(dest), { recursive: true });\n // The landscape is staged from the bytes this commit writes, never from\n // the file on disk — which may be the scaffold's stub, a stamped\n // predecessor, or absent — so the project parsed is the one the grader\n // will read after the commit.\n if (path === landscape) await writeFile(dest, req.landscape, \"utf8\");\n else await copyFile(path, dest);\n staged.push(dest);\n }\n const project = await loadProject(root, staged);\n // RELATIONSHIPS travel with the elements, and dropping them is silent: they\n // are what each per-service overview computes its peer set from, so a facts\n // record without them renders every service view peerless — the service and\n // its own interior, and nothing it touches — with no error anywhere\n // (`core/repo/tree/render/views.ts` states the consequence beside the\n // optional field). The fallback `req.map` is the whole self-check document\n // and already carries them.\n return project.clean\n ? { elements: project.elements, globalStyles: project.globalStyles, relationships: project.relationships }\n : req.map;\n } catch {\n // `loadProject` throws on an error it cannot attribute to a document, and\n // a full disk fails the staging above; both are the fail-closed arm.\n return req.map;\n } finally {\n if (root !== null) await rm(root, { recursive: true, force: true });\n }\n}\n\nexport interface SeedPlan {\n writes: PlannedWrite[];\n /** Service ids split by whether this run creates the directory. */\n services: { created: string[]; existing: string[] };\n /**\n * Repo-relative paths of the generated views files that hold a document loam\n * did NOT write — the plan's own refusal, handed to `./commit.ts` rather than\n * spelled here, because this module decides and never announces.\n *\n * Empty on every ordinary run. Non-empty means the whole seed must be refused\n * before a byte lands: see `stageGenerated`.\n */\n authored: string[];\n}\n\n/**\n * Does this existing service directory hold nothing git would keep? An empty\n * directory is a service to the walk but nothing at all to git, so a landscape\n * declaring it clones into a repo where `validate --all` fails on a service\n * that exists only on the author's disk. Seed's own crash window produces\n * exactly this tree — `stageWrites` creates directories before the journal is\n * written, so a kill in between leaves directories no recovery knows about —\n * and so does a plain `mkdir`. Staging temps do not count as content: they are\n * that same crash's litter, not somebody's file.\n */\nasync function keepsNothing(dir: string): Promise<boolean> {\n try {\n return (await readdir(dir)).every((entry) => TEMP_FILE_RE.test(entry));\n } catch {\n // Unreadable is not empty: the walk found this directory a moment ago, and\n // inventing a write into a directory we cannot list is the wrong guess.\n return false;\n }\n}\n\n/**\n * The complete plan, in memory, before a byte lands: the sealed landscape, one\n * marker per NEW subsystem, one `.gitkeep` per service directory this commit\n * creates — and per existing one holding nothing git would keep (see\n * `keepsNothing`) — and the generated views file rendered from the tree AS\n * THIS COMMIT LEAVES IT, never from a re-walk between two states\n * (commands/subsystem/txn/views.ts's `movedTree` doctrine). That last one is\n * the only write that can be a DELETE: a tree with no subsystems owes no views\n * file, and a leftover is a finding.\n *\n * Existing directories are never moved: fleet.yaml's placement is ignored for\n * a service that already exists — moving is `loam subsystem move <id> --into\n * <name>`'s job, under its own guards — and nothing anywhere is deleted except\n * that one generated file.\n */\nexport async function planSeedWrites(req: SeedPlanInput, tree: FleetTree): Promise<SeedPlan> {\n const { docsDir, seed } = req;\n const writes: PlannedWrite[] = [planWrite(landscapePath(docsDir), req.landscape)];\n\n const existingSubs = new Map(tree.subsystems.map((s) => [s.name, s]));\n const newSubs: SubsystemEntry[] = [];\n for (const sub of seed.subsystems) {\n if (existingSubs.has(sub as string)) continue;\n const dir = subsystemPathUnder(servicesDir(docsDir), sub);\n // A comment line parses to null — a valid empty marker (tree/marker.ts).\n writes.push(planWrite(join(dir, SUBSYSTEM_MARKER), \"# subsystem marker\\n\"));\n newSubs.push({ name: sub as string, path: [sub as string], dir, meta: {} });\n }\n\n const treeDirs = new Map(tree.services.map((s) => [s.id as string, s.dir]));\n // Every existing directory inspected at once — independent reads, so the\n // loop below stays synchronous.\n const invisible = new Map(\n await Promise.all([...treeDirs].map(async ([id, dir]) => [id, await keepsNothing(dir)] as const)),\n );\n const created: string[] = [];\n const existing: string[] = [];\n const newWalked: WalkedService[] = [];\n for (const s of seed.services) {\n const already = treeDirs.get(s.id as string);\n if (already !== undefined) {\n existing.push(s.id);\n // Existing, but git-invisible: give it the `.gitkeep` a created service\n // gets, or the landscape about to be written declares a directory the\n // next clone will not have. `planWrite` makes this a no-op when the file\n // is already there, so a real service is never touched.\n if (invisible.get(s.id as string) === true) writes.push(planWrite(join(already, \".gitkeep\"), \"\"));\n continue;\n }\n created.push(s.id);\n const parent = s.subsystem === null ? null : existingSubs.get(s.subsystem as string) ?? null;\n const dir =\n s.subsystem === null\n ? unfiledServicePaths(docsDir, s.id).dir\n : parent !== null\n ? servicePathsUnder(parent.dir, s.id).dir\n : // A subsystem this same commit creates: the creation spelling,\n // through the same branded joins the subsystem verbs use.\n serviceDirOf(join(subsystemPathUnder(servicesDir(docsDir), s.subsystem), s.id));\n writes.push(planWrite(join(dir, \".gitkeep\"), \"\"));\n newWalked.push({\n // A ServiceId IS a RawServiceId (the checked subtype), so no re-cast.\n id: s.id,\n dir,\n subsystem: s.subsystem === null ? [] : parent !== null ? parent.path : [s.subsystem as string],\n });\n }\n\n const plannedTree: FleetTree = {\n findings: [],\n services: [...tree.services, ...newWalked],\n subsystems: [...tree.subsystems, ...newSubs],\n };\n // The generated views file, including its ABSENCE. `renderSubsystemViews`\n // returning null means the tree has no subsystems, and its contract is that\n // the file must then not exist — `validate` grades a leftover as\n // `subsystem.views-stale`, so a seed that ignored the file would exit 0 and\n // send the caller, via its own `next` list, into a failing gate over a file\n // it had in hand. `content: null` is the delete the transaction understands\n // (commands/subsystem/txn/txn.ts's `\"removed\"`), and a file that already\n // SAYS this is left out of the journal entirely rather than written back\n // over itself.\n //\n // \"Already says this\" is content, not bytes: `viewsAgree` performs the\n // comparison and records the Windows reason for it, beside the generator\n // that mints the bytes. What gets WRITTEN is unchanged — the LF render, as a\n // Buffer — so this decode is a comparison only, never the string round trip\n // to disk that writes.ts forbids; a views file whose bytes are not UTF-8\n // therefore fails the compare and is regenerated, which is the right answer\n // for it anyway.\n //\n // BOTH generated files, from ONE read of the project: `architecture/services.likec4`\n // is the same contract one file over — one overview view per service the map\n // draws, absent when it draws none — and a seed that landed only the subsystem\n // views would send its caller, through its own `next` list, into a\n // `subsystem.views-stale` no `seed` could clear. This is where the seeded\n // services first exist AND the sealed landscape first says what they are, so\n // it is the only moment the pair can be rendered together.\n const facts = await projectFacts(req);\n const authored = [\n await stageGenerated(writes, subsystemViewsPath(docsDir), renderSubsystemViews(plannedTree, facts)),\n await stageGenerated(writes, serviceViewsPath(docsDir), renderServiceOverviews(plannedTree, facts)),\n ].filter((path): path is string => path !== null);\n\n return {\n writes,\n services: { created: created.sort(), existing: existing.sort() },\n authored: authored.map((path) => repoPath(docsDir, path)),\n };\n}\n\n/**\n * Append the write for ONE generated file, or nothing when the file already\n * says it — the comparison the block above argues for, spelled once so the two\n * generated files cannot drift into two rules. Answers the path when the file\n * standing there is SOMEBODY ELSE'S, and stages nothing for it.\n *\n * OWNERSHIP FIRST, before the compare, exactly as `commands/subsystem/sync.ts`\n * asks it — through the same reader, from beneath every writer\n * (`core/repo/tree/render/service/header.ts`). Both of these are filenames loam\n * RESERVES inside `architecture/`, a directory the team owns, and seed rewrites\n * them whole; until this line existed a `loam seed` into a repository that kept\n * a hand-authored diagram at one of those paths destroyed it without a word.\n * That is the same promise `seed-landscape-edited` makes one file over — seed\n * never overwrites human work — and it was missing for the two files seed\n * reserves rather than shares.\n *\n * It is checked before the compare and not after, so that \"the file already says\n * this\" can never vouch for a file loam did not write. An authored file cannot\n * in fact agree with a render that opens with loam's mark, but a reader must not\n * have to prove that to know the guard holds.\n */\nasync function stageGenerated(writes: PlannedWrite[], path: string, content: string | null): Promise<string | null> {\n const { text: onDisk, authored } = await readGeneratedFile(path);\n if (authored) return path;\n if (viewsAgree(onDisk, content)) return null;\n writes.push({ path, content: content === null ? null : Buffer.from(content, \"utf8\") });\n return null;\n}\n"]}
@@ -59,7 +59,12 @@ export function registerSeed(program) {
59
59
  const seed = read.seed;
60
60
  // The self-check: the emitted DSL is parsed in memory BEFORE anything
61
61
  // is written, so a templater bug can never land as a broken landscape.
62
- // Its parsed elements are reused for the generated views join.
62
+ // Its parsed document travels to the plan as the landscape's OWN facts —
63
+ // the render's input when no other `architecture/` document exists, and
64
+ // the fallback otherwise. It is not the render's whole input: the global
65
+ // style ids the render asks for are the PROJECT's (the template declares
66
+ // no `global`, but a sibling document may), which `plan.ts`'s
67
+ // `projectFacts` reads the way `validate --all` will.
63
68
  const sealed = sealLandscape(renderLandscape(seed));
64
69
  const doc = await loadSource(sealed);
65
70
  const firstError = doc.errors[0];
@@ -71,7 +76,7 @@ export function registerSeed(program) {
71
76
  docsDir,
72
77
  seed,
73
78
  landscape: sealed,
74
- elements: doc.elements,
79
+ map: doc,
75
80
  rerun: `loam seed --from ${fromArg}`,
76
81
  });
77
82
  if (!committed.ok)
@@ -1 +1 @@
1
- {"version":3,"file":"seed.js","sourceRoot":"","sources":["../../../src/commands/seed/seed.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `loam seed` — the fleet map's first hour, mechanically: a tiny\n * human-authored fleet.yaml (service ids, optional subsystems, externals and\n * `a -> b` calls) templated into architecture/landscape.likec4 plus one\n * services/<id>/ directory per service, in one journaled transaction.\n *\n * NOT an extractor, deliberately: a human stated every fact in the file —\n * which services exist and who calls whom is the one thing no generator can\n * read off a repository (core/docs.ts's landscape doctrine) — and loam does\n * mechanical templating over those statements, the same category as\n * `loam new`. Seed never overwrites human work: it writes the landscape only\n * when the file is absent, is the scaffold's untouched stub, or carries its\n * own verified line-1 stamp (`core/c4/seed/stamp.ts`); everything else\n * refuses `seed-landscape-edited`.\n */\nimport type { Command } from \"commander\";\nimport { resolve } from \"node:path\";\nimport { loadSource } from \"../../core/c4/likec4.js\";\nimport { readFleetFile, type SeedFileProblem } from \"../../core/c4/seed/fleet-file.js\";\nimport { sealLandscape } from \"../../core/c4/seed/stamp.js\";\nimport { renderLandscape } from \"../../core/c4/seed/template.js\";\nimport { loadConfig } from \"../../core/envelope/config.js\";\nimport { emitJson, emitJsonError, fail, repoPath, reportNoConfig } from \"../../core/envelope/json.js\";\nimport { NotUtf8Error, readUtf8 } from \"../../core/staging/writes.js\";\nimport { sayRecovered } from \"../policy/format.js\";\nimport { docsRepoReady, reportDocsRepoError } from \"../policy/gate.js\";\nimport { serviceTreePathOf } from \"../../core/repo/service-target.js\";\nimport { commitSeed, type LandscapeDisposition, type SeedCommit } from \"./commit.js\";\n\ninterface SeedOptions {\n from?: string;\n json?: boolean;\n}\n\nexport function registerSeed(program: Command): void {\n program\n .command(\"seed\")\n .description(\n \"Template the fleet map and service directories from a tiny human-authored fleet.yaml\",\n )\n .option(\"--from <file>\", \"the fleet file to read (default: fleet.yaml in the current directory)\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .action(async (opts: SeedOptions) => {\n const json = opts.json === true;\n const fromArg = opts.from ?? \"fleet.yaml\";\n\n const loaded = await loadConfig();\n if (loaded.kind !== \"loaded\") {\n reportNoConfig(json, loaded);\n return;\n }\n const { docsDir } = loaded.config;\n // `services`, not `docs`: every id in fleet.yaml becomes a\n // services/<id>/ directory, and the under-lock preflight enumerates the\n // tree — a run without the directory that IS the fleet is guessing.\n if (!docsRepoReady(json, docsDir, \"services\")) return;\n\n // The fleet file is INPUT only — read once, from wherever the caller\n // keeps it (it may legitimately live outside both repos), never stored\n // and never written back. Refusals name the spelling the caller typed.\n let text: string;\n try {\n text = await readUtf8(resolve(fromArg));\n } catch (err) {\n if (err instanceof NotUtf8Error) return fail(json, \"seed-file-invalid\", err.message);\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\") {\n return fail(\n json,\n \"seed-file-invalid\",\n `${fromArg} does not exist — write the fleet file (a services: list of ids, plus ` +\n `optional subsystems:, externals: and calls: like 'checkout -> payments'), or ` +\n `point --from at where it lives. Nothing was written.`,\n );\n }\n // An errno is a filesystem answer (EISDIR, EACCES, …) about the named\n // file; anything without one is a real bug and stays `internal`.\n if (e.path !== undefined || typeof e.errno === \"number\") {\n return fail(\n json,\n \"seed-file-invalid\",\n `${fromArg} could not be read as the fleet file — ${e.message}. Nothing was written.`,\n );\n }\n throw err;\n }\n\n const read = readFleetFile(text, fromArg);\n if (!read.ok) return refuseFleetFile(json, read.problem);\n const seed = read.seed;\n\n // The self-check: the emitted DSL is parsed in memory BEFORE anything\n // is written, so a templater bug can never land as a broken landscape.\n // Its parsed elements are reused for the generated views join.\n const sealed = sealLandscape(renderLandscape(seed));\n const doc = await loadSource(sealed);\n const firstError = doc.errors[0];\n if (firstError !== undefined) {\n return fail(\n json,\n \"internal\",\n `loam's own templater produced a landscape that does not parse — a bug in loam, not ` +\n `in ${fromArg}; nothing was written. First error: ${firstError.message}`,\n );\n }\n\n const committed = await commitSeed({\n docsDir,\n seed,\n landscape: sealed,\n elements: doc.elements,\n rerun: `loam seed --from ${fromArg}`,\n });\n if (!committed.ok) return refuseCommit(json, committed);\n\n const written = committed.written.map((w) => repoPath(docsDir, w));\n const removed = committed.removed.map((w) => repoPath(docsDir, w));\n const firstCreated = committed.services.created[0];\n const next = [\n \"loam validate --all\",\n \"loam list --needs-work\",\n // Interpolation is agent-safe: the id already passed the grammar.\n ...(firstCreated === undefined ? [] : [`loam adopt --service ${firstCreated}`]),\n ];\n\n if (json) {\n emitJson({\n command: \"seed\",\n fleetFile: fromArg,\n landscape: committed.landscape,\n created: written,\n removed,\n services: committed.services,\n subsystems: [...seed.subsystems].sort(),\n externals: [...seed.externals].sort(),\n calls: seed.calls.length,\n next,\n ...(committed.recovered === null ? {} : { recovered: committed.recovered }),\n });\n return;\n }\n if (committed.recovered !== null && committed.recovered.outcome !== \"consistent\") {\n console.log(`note: ${sayRecovered(committed.recovered)}`);\n }\n console.log(\n `architecture/landscape.likec4 ${LANDSCAPE_SENTENCE[committed.landscape]} — ` +\n `${seed.services.length} service(s), ${seed.calls.length} call(s), from ${fromArg}`,\n );\n for (const w of written) console.log(` + ${w}`);\n // Only ever the generated views file, and only when the resulting tree\n // has no subsystems left to view — but a removal printed as `+` would be\n // a lie, and an unmentioned one a surprise in `git status`.\n for (const w of removed) console.log(` - ${w} (generated, and this tree has no subsystems)`);\n for (const id of committed.services.existing) {\n // Spelled from the enumeration, and this line is the one place it matters\n // most: the sentence is ABOUT placement, so naming the root form would\n // say \"left exactly where it is\" while pointing at somewhere it is not.\n const at = await serviceTreePathOf(docsDir, id);\n console.log(\n ` = ${at}/ already exists — left exactly where it is (fleet.yaml's ` +\n `placement is ignored for an existing service; \\`loam subsystem move ${id} ` +\n `--into <name>\\` is how one moves)`,\n );\n }\n console.log(\"\\nNext:\");\n for (const n of next) console.log(` ${n}`);\n });\n}\n\nconst LANDSCAPE_SENTENCE: Record<LandscapeDisposition, string> = {\n created: \"created\",\n \"replaced-stub\": \"written (replaced the scaffold's untouched stub)\",\n regenerated: \"regenerated (the line-1 stamp proved no hand edits)\",\n};\n\n/** One stable code per problem kind — the mapping the fleet-file union exists for. */\nfunction refuseFleetFile(json: boolean, problem: SeedFileProblem): void {\n switch (problem.kind) {\n case \"invalid\":\n return fail(json, \"seed-file-invalid\", problem.message);\n case \"duplicate\":\n return fail(json, \"seed-duplicate-service\", problem.message);\n case \"unknown-subsystem\":\n return fail(json, \"seed-unknown-subsystem\", problem.message);\n case \"unknown-endpoint\":\n // The existing code, not a fifth new one: the caller's action is the\n // same as for every other unknown service — fix the name, re-run.\n return fail(json, \"unknown-service\", problem.message);\n }\n // Exhaustiveness checked, not assumed, and the branch is genuinely dead:\n // without it a fifth `kind` falls out of the switch printing nothing and\n // leaving `process.exitCode` unset — a refused fleet file exiting 0, which\n // is the worst answer available.\n const unreachable: never = problem;\n throw new Error(`refuseFleetFile: no code for fleet-file problem '${JSON.stringify(unreachable)}'`);\n}\n\nfunction refuseCommit(json: boolean, committed: Exclude<SeedCommit, { ok: true }>): void {\n if (\"repoGone\" in committed) return reportDocsRepoError(json, committed.repoGone);\n if (committed.code === \"seed-file-invalid\") {\n // The exact ids to paste ride as data beside the prose, so an agent can\n // repair fleet.yaml without parsing a sentence.\n if (json) {\n emitJsonError(\"seed-file-invalid\", committed.message, {\n missingServices: committed.missingServices,\n });\n return;\n }\n return fail(json, \"seed-file-invalid\", committed.message);\n }\n return fail(json, committed.code, committed.message);\n}\n"]}
1
+ {"version":3,"file":"seed.js","sourceRoot":"","sources":["../../../src/commands/seed/seed.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `loam seed` — the fleet map's first hour, mechanically: a tiny\n * human-authored fleet.yaml (service ids, optional subsystems, externals and\n * `a -> b` calls) templated into architecture/landscape.likec4 plus one\n * services/<id>/ directory per service, in one journaled transaction.\n *\n * NOT an extractor, deliberately: a human stated every fact in the file —\n * which services exist and who calls whom is the one thing no generator can\n * read off a repository (core/docs.ts's landscape doctrine) — and loam does\n * mechanical templating over those statements, the same category as\n * `loam new`. Seed never overwrites human work: it writes the landscape only\n * when the file is absent, is the scaffold's untouched stub, or carries its\n * own verified line-1 stamp (`core/c4/seed/stamp.ts`); everything else\n * refuses `seed-landscape-edited`.\n */\nimport type { Command } from \"commander\";\nimport { resolve } from \"node:path\";\nimport { loadSource } from \"../../core/c4/likec4.js\";\nimport { readFleetFile, type SeedFileProblem } from \"../../core/c4/seed/fleet-file.js\";\nimport { sealLandscape } from \"../../core/c4/seed/stamp.js\";\nimport { renderLandscape } from \"../../core/c4/seed/template.js\";\nimport { loadConfig } from \"../../core/envelope/config.js\";\nimport { emitJson, emitJsonError, fail, repoPath, reportNoConfig } from \"../../core/envelope/json.js\";\nimport { NotUtf8Error, readUtf8 } from \"../../core/staging/writes.js\";\nimport { sayRecovered } from \"../policy/format.js\";\nimport { docsRepoReady, reportDocsRepoError } from \"../policy/gate.js\";\nimport { serviceTreePathOf } from \"../../core/repo/service-target.js\";\nimport { commitSeed, type LandscapeDisposition, type SeedCommit } from \"./commit.js\";\n\ninterface SeedOptions {\n from?: string;\n json?: boolean;\n}\n\nexport function registerSeed(program: Command): void {\n program\n .command(\"seed\")\n .description(\n \"Template the fleet map and service directories from a tiny human-authored fleet.yaml\",\n )\n .option(\"--from <file>\", \"the fleet file to read (default: fleet.yaml in the current directory)\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .action(async (opts: SeedOptions) => {\n const json = opts.json === true;\n const fromArg = opts.from ?? \"fleet.yaml\";\n\n const loaded = await loadConfig();\n if (loaded.kind !== \"loaded\") {\n reportNoConfig(json, loaded);\n return;\n }\n const { docsDir } = loaded.config;\n // `services`, not `docs`: every id in fleet.yaml becomes a\n // services/<id>/ directory, and the under-lock preflight enumerates the\n // tree — a run without the directory that IS the fleet is guessing.\n if (!docsRepoReady(json, docsDir, \"services\")) return;\n\n // The fleet file is INPUT only — read once, from wherever the caller\n // keeps it (it may legitimately live outside both repos), never stored\n // and never written back. Refusals name the spelling the caller typed.\n let text: string;\n try {\n text = await readUtf8(resolve(fromArg));\n } catch (err) {\n if (err instanceof NotUtf8Error) return fail(json, \"seed-file-invalid\", err.message);\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\") {\n return fail(\n json,\n \"seed-file-invalid\",\n `${fromArg} does not exist — write the fleet file (a services: list of ids, plus ` +\n `optional subsystems:, externals: and calls: like 'checkout -> payments'), or ` +\n `point --from at where it lives. Nothing was written.`,\n );\n }\n // An errno is a filesystem answer (EISDIR, EACCES, …) about the named\n // file; anything without one is a real bug and stays `internal`.\n if (e.path !== undefined || typeof e.errno === \"number\") {\n return fail(\n json,\n \"seed-file-invalid\",\n `${fromArg} could not be read as the fleet file — ${e.message}. Nothing was written.`,\n );\n }\n throw err;\n }\n\n const read = readFleetFile(text, fromArg);\n if (!read.ok) return refuseFleetFile(json, read.problem);\n const seed = read.seed;\n\n // The self-check: the emitted DSL is parsed in memory BEFORE anything\n // is written, so a templater bug can never land as a broken landscape.\n // Its parsed document travels to the plan as the landscape's OWN facts —\n // the render's input when no other `architecture/` document exists, and\n // the fallback otherwise. It is not the render's whole input: the global\n // style ids the render asks for are the PROJECT's (the template declares\n // no `global`, but a sibling document may), which `plan.ts`'s\n // `projectFacts` reads the way `validate --all` will.\n const sealed = sealLandscape(renderLandscape(seed));\n const doc = await loadSource(sealed);\n const firstError = doc.errors[0];\n if (firstError !== undefined) {\n return fail(\n json,\n \"internal\",\n `loam's own templater produced a landscape that does not parse — a bug in loam, not ` +\n `in ${fromArg}; nothing was written. First error: ${firstError.message}`,\n );\n }\n\n const committed = await commitSeed({\n docsDir,\n seed,\n landscape: sealed,\n map: doc,\n rerun: `loam seed --from ${fromArg}`,\n });\n if (!committed.ok) return refuseCommit(json, committed);\n\n const written = committed.written.map((w) => repoPath(docsDir, w));\n const removed = committed.removed.map((w) => repoPath(docsDir, w));\n const firstCreated = committed.services.created[0];\n const next = [\n \"loam validate --all\",\n \"loam list --needs-work\",\n // Interpolation is agent-safe: the id already passed the grammar.\n ...(firstCreated === undefined ? [] : [`loam adopt --service ${firstCreated}`]),\n ];\n\n if (json) {\n emitJson({\n command: \"seed\",\n fleetFile: fromArg,\n landscape: committed.landscape,\n created: written,\n removed,\n services: committed.services,\n subsystems: [...seed.subsystems].sort(),\n externals: [...seed.externals].sort(),\n calls: seed.calls.length,\n next,\n ...(committed.recovered === null ? {} : { recovered: committed.recovered }),\n });\n return;\n }\n if (committed.recovered !== null && committed.recovered.outcome !== \"consistent\") {\n console.log(`note: ${sayRecovered(committed.recovered)}`);\n }\n console.log(\n `architecture/landscape.likec4 ${LANDSCAPE_SENTENCE[committed.landscape]} — ` +\n `${seed.services.length} service(s), ${seed.calls.length} call(s), from ${fromArg}`,\n );\n for (const w of written) console.log(` + ${w}`);\n // Only ever the generated views file, and only when the resulting tree\n // has no subsystems left to view — but a removal printed as `+` would be\n // a lie, and an unmentioned one a surprise in `git status`.\n for (const w of removed) console.log(` - ${w} (generated, and this tree has no subsystems)`);\n for (const id of committed.services.existing) {\n // Spelled from the enumeration, and this line is the one place it matters\n // most: the sentence is ABOUT placement, so naming the root form would\n // say \"left exactly where it is\" while pointing at somewhere it is not.\n const at = await serviceTreePathOf(docsDir, id);\n console.log(\n ` = ${at}/ already exists — left exactly where it is (fleet.yaml's ` +\n `placement is ignored for an existing service; \\`loam subsystem move ${id} ` +\n `--into <name>\\` is how one moves)`,\n );\n }\n console.log(\"\\nNext:\");\n for (const n of next) console.log(` ${n}`);\n });\n}\n\nconst LANDSCAPE_SENTENCE: Record<LandscapeDisposition, string> = {\n created: \"created\",\n \"replaced-stub\": \"written (replaced the scaffold's untouched stub)\",\n regenerated: \"regenerated (the line-1 stamp proved no hand edits)\",\n};\n\n/** One stable code per problem kind — the mapping the fleet-file union exists for. */\nfunction refuseFleetFile(json: boolean, problem: SeedFileProblem): void {\n switch (problem.kind) {\n case \"invalid\":\n return fail(json, \"seed-file-invalid\", problem.message);\n case \"duplicate\":\n return fail(json, \"seed-duplicate-service\", problem.message);\n case \"unknown-subsystem\":\n return fail(json, \"seed-unknown-subsystem\", problem.message);\n case \"unknown-endpoint\":\n // The existing code, not a fifth new one: the caller's action is the\n // same as for every other unknown service — fix the name, re-run.\n return fail(json, \"unknown-service\", problem.message);\n }\n // Exhaustiveness checked, not assumed, and the branch is genuinely dead:\n // without it a fifth `kind` falls out of the switch printing nothing and\n // leaving `process.exitCode` unset — a refused fleet file exiting 0, which\n // is the worst answer available.\n const unreachable: never = problem;\n throw new Error(`refuseFleetFile: no code for fleet-file problem '${JSON.stringify(unreachable)}'`);\n}\n\nfunction refuseCommit(json: boolean, committed: Exclude<SeedCommit, { ok: true }>): void {\n if (\"repoGone\" in committed) return reportDocsRepoError(json, committed.repoGone);\n if (committed.code === \"seed-file-invalid\") {\n // The exact ids to paste ride as data beside the prose, so an agent can\n // repair fleet.yaml without parsing a sentence.\n if (json) {\n emitJsonError(\"seed-file-invalid\", committed.message, {\n missingServices: committed.missingServices,\n });\n return;\n }\n return fail(json, \"seed-file-invalid\", committed.message);\n }\n return fail(json, committed.code, committed.message);\n}\n"]}
@@ -31,7 +31,8 @@ import { featureStatus } from "../../core/status/feature/feature.js";
31
31
  import { executableNext } from "../../core/status/actions/execution.js";
32
32
  import { findingJson } from "../../core/vocabulary/report.js";
33
33
  import { plural } from "../policy/format.js";
34
- import { errorText, mark } from "./marks.js";
34
+ import { mark } from "./marks.js";
35
+ import { errorText } from "../../core/c4/likec4.js";
35
36
  const REVIEW_INTENT_LIMIT = 1_200;
36
37
  function intentSummary(text) {
37
38
  const withoutFrontmatter = text.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n/, "");
@@ -1 +1 @@
1
- {"version":3,"file":"feature.js","sourceRoot":"","sources":["../../../src/commands/show/feature.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One feature as `loam show` presents it: its intent, its delta, the\n * per-service specs under it, and the business promises it changes.\n *\n * Split from `./service.ts` because a service and a feature answer different\n * questions — what a repository holds now, against what a change proposes —\n * and only the service view has to reach the landscape for its edges. The\n * one-line shapes both write in are in `./marks.ts`.\n *\n * THE CAPABILITY SECTION IS NOT DECORATION. Without it a feature carrying only\n * a `features/<FEAT>/capabilities/<id>/spec.md` — a business change with no\n * service touched yet, which is the first thing an analyst writes and what\n * `loam new --capability` scaffolds — displayed as a feature that carries\n * nothing at all, from the command a person runs to find out what a feature\n * carries. The archive would then refuse it (`capability.uncovered`,\n * `scaffold.placeholder`) over documents this view said were not there.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { emitJson, repoPath } from \"../../core/envelope/json.js\";\nimport { type Elem } from \"../../core/c4/likec4.js\";\nimport { FleetContext } from \"../../core/fleet-context.js\";\nimport { type FeatureEntry } from \"../../core/repo/entries.js\";\nimport { featurePaths, featureSpecPaths } from \"../../core/repo/paths.js\";\nimport { parseRequirements } from \"../../core/document/parse.js\";\nimport { featureCapabilityDeltas } from \"../../core/capabilities/delta/tree.js\";\nimport { capabilityDeltaSummaries } from \"../../core/capabilities/delta/summary.js\";\nimport { apiChanges } from \"../../core/projection/api.js\";\nimport { eventChanges } from \"../../core/projection/events.js\";\nimport { featureStatus } from \"../../core/status/feature/feature.js\";\nimport { executableNext } from \"../../core/status/actions/execution.js\";\nimport { findingJson } from \"../../core/vocabulary/report.js\";\nimport type { DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport { plural } from \"../policy/format.js\";\nimport { errorText, mark } from \"./marks.js\";\n\nconst REVIEW_INTENT_LIMIT = 1_200;\n\nfunction intentSummary(text: string): string {\n const withoutFrontmatter = text.replace(/^---\\r?\\n[\\s\\S]*?\\r?\\n---\\r?\\n/, \"\");\n const prose = withoutFrontmatter\n .replace(/<!--[\\s\\S]*?-->/g, \"\")\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter((line) => line.length > 0 && !line.startsWith(\"#\"))\n .join(\" \");\n return prose.length <= REVIEW_INTENT_LIMIT\n ? prose\n : `${prose.slice(0, REVIEW_INTENT_LIMIT - 1).trimEnd()}…`;\n}\n\nexport async function showFeature(\n docsDir: DocsDir,\n feature: FeatureEntry,\n json: boolean,\n context = new FleetContext(),\n): Promise<void> {\n const paths = featurePaths(feature.dir);\n const delta = feature.has.delta\n ? await context.loadLikeC4(paths.delta)\n : { errors: [], elements: [] as Elem[], relationships: [] };\n const taggedEls = delta.elements.filter((e) => e.tags.includes(feature.id));\n const taggedRels = delta.relationships.filter((r) => r.tags.includes(feature.id));\n\n const services = [];\n for (const svc of feature.services) {\n const servicePaths = featureSpecPaths(feature.dir, svc);\n const specPath = servicePaths.spec;\n const reqs = existsSync(specPath) ? parseRequirements(await readFile(specPath, \"utf8\")) : [];\n services.push({\n id: svc,\n added: reqs.filter((r) => r.kind === \"ADDED\").length,\n modified: reqs.filter((r) => r.kind === \"MODIFIED\").length,\n removed: reqs.filter((r) => r.kind === \"REMOVED\").length,\n operations: [...new Set(reqs.flatMap((r) => r.operations))],\n api: await apiChanges(servicePaths.openapi),\n events: await eventChanges(servicePaths.asyncapi),\n });\n }\n\n // One `existsSync` for a feature with no capability delta, which is every\n // feature in a fleet that has not adopted the business axis — the walk's own\n // short-circuit, and the whole cost such a fleet pays for this section.\n const capabilities = await capabilityDeltaSummaries(\n (await featureCapabilityDeltas(feature.dir)).docs,\n async (path) => parseRequirements(await readFile(path, \"utf8\")),\n );\n\n if (json) {\n const status = await featureStatus(docsDir, feature, { context });\n const issues = status.checks.issues.map((finding) =>\n findingJson(finding, { path: status.feature.path, role: \"scope\" }),\n );\n const blockers = issues.filter(\n (finding) => finding[\"severity\"] === \"error\" || finding[\"gates\"] === true,\n );\n const intent = existsSync(paths.intent) ? intentSummary(await readFile(paths.intent, \"utf8\")) : \"\";\n emitJson({\n command: \"show\",\n type: \"feature\",\n id: feature.id,\n dirName: feature.dirName,\n path: repoPath(docsDir, feature.dir),\n archived: feature.archived,\n has: feature.has,\n delta: {\n elements: taggedEls.length,\n relationships: taggedRels.length,\n errors: delta.errors.map(errorText),\n },\n services,\n // Additive, which the envelope permits (core/envelope/json.ts), and an\n // EMPTY ARRAY rather than an omitted key for a feature that carries none:\n // a consumer indexing it must not have to tell \"no capability delta\" from\n // \"this loam does not report them\", and the two would be the same absence.\n capabilities: capabilities.map((c) => ({\n id: c.id,\n path: repoPath(docsDir, c.spec),\n added: c.added,\n modified: c.modified,\n removed: c.removed,\n promises: c.promises,\n })),\n review: {\n readyToArchive: !feature.archived && status.feature.stage === \"done\" && blockers.length === 0,\n intent: {\n path: repoPath(docsDir, paths.intent),\n summary: intent,\n },\n architecture: {\n path: repoPath(docsDir, paths.delta),\n elements: taggedEls.map((element) => ({\n id: element.id,\n kind: element.kind,\n title: element.title,\n })),\n relationships: taggedRels.map((relationship) => ({\n source: relationship.source,\n target: relationship.target,\n title: relationship.title ?? null,\n operation: relationship.op ?? null,\n })),\n errors: delta.errors.map(errorText),\n },\n services,\n dependencies: { blockedBy: status.feature.blockedBy },\n artifacts: status.artifacts,\n verification: status.verification,\n blockers,\n advisories: issues.filter(\n (finding) => finding[\"severity\"] === \"warn\" && finding[\"gates\"] !== true,\n ),\n useCases: status.useCases,\n next: status.next.map(executableNext),\n },\n });\n return;\n }\n\n const state = feature.archived ? \"archived\" : \"active\";\n console.log(`${feature.id} ${repoPath(docsDir, feature.dir)} ${state}\\n`);\n\n console.log(\" artifacts\");\n console.log(` ${mark(feature.has.intent)} intent.md`);\n const deltaNote =\n delta.errors.length > 0\n ? `${delta.errors.length} error(s)`\n : `${plural(taggedEls.length, \"element\")} · ${plural(taggedRels.length, \"relationship\")} tagged ${feature.id}`;\n console.log(` ${mark(feature.has.delta)} delta.likec4 ${feature.has.delta ? deltaNote : \"\"}`.trimEnd());\n\n if (services.length > 0) {\n console.log(\"\\n services\");\n const width = Math.max(...services.map((s) => s.id.length));\n for (const s of services) {\n const ops = s.operations.length > 0 ? ` · ${s.operations.join(\", \")}` : \"\";\n console.log(\n ` ${s.id.padEnd(width)} +${s.added} ~${s.modified} -${s.removed} requirements${ops}`,\n );\n }\n }\n\n // Printed only when the feature carries one: a fleet that has not adopted the\n // business axis must see nothing, and an empty \"capabilities\" heading over\n // every feature in every such fleet is exactly the noise that teaches people\n // to stop reading the output.\n if (capabilities.length > 0) {\n console.log(\"\\n capabilities\");\n const width = Math.max(...capabilities.map((c) => c.id.length));\n for (const c of capabilities) {\n // The promise ids, not just the counts: `Realizes: <capability>#<id>` is\n // the line somebody has to write next, and the id is the half of it a\n // reader cannot guess.\n const ids = c.promises.flatMap((p) => (p.id === null ? [] : [p.id]));\n console.log(\n ` ${c.id.padEnd(width)} +${c.added} ~${c.modified} -${c.removed} promises` +\n (ids.length === 0 ? \"\" : ` · ${ids.join(\", \")}`),\n );\n }\n }\n\n if (delta.errors.length > 0) {\n console.log(\"\\n delta errors\");\n for (const e of delta.errors) console.log(` ✗ ${errorText(e)}`);\n }\n}\n\n/* ------------------------------------------------------------------ */\n\n/** \"-\" for absent, matching `list`: ✗ is the error glyph here, and a missing runbook is not an error. */\n"]}
1
+ {"version":3,"file":"feature.js","sourceRoot":"","sources":["../../../src/commands/show/feature.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One feature as `loam show` presents it: its intent, its delta, the\n * per-service specs under it, and the business promises it changes.\n *\n * Split from `./service.ts` because a service and a feature answer different\n * questions — what a repository holds now, against what a change proposes —\n * and only the service view has to reach the landscape for its edges. The\n * one-line shapes both write in are in `./marks.ts`.\n *\n * THE CAPABILITY SECTION IS NOT DECORATION. Without it a feature carrying only\n * a `features/<FEAT>/capabilities/<id>/spec.md` — a business change with no\n * service touched yet, which is the first thing an analyst writes and what\n * `loam new --capability` scaffolds — displayed as a feature that carries\n * nothing at all, from the command a person runs to find out what a feature\n * carries. The archive would then refuse it (`capability.uncovered`,\n * `scaffold.placeholder`) over documents this view said were not there.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { emitJson, repoPath } from \"../../core/envelope/json.js\";\nimport { type Elem } from \"../../core/c4/likec4.js\";\nimport { FleetContext } from \"../../core/fleet-context.js\";\nimport { type FeatureEntry } from \"../../core/repo/entries.js\";\nimport { featurePaths, featureSpecPaths } from \"../../core/repo/paths.js\";\nimport { parseRequirements } from \"../../core/document/parse.js\";\nimport { featureCapabilityDeltas } from \"../../core/capabilities/delta/tree.js\";\nimport { capabilityDeltaSummaries } from \"../../core/capabilities/delta/summary.js\";\nimport { apiChanges } from \"../../core/projection/api.js\";\nimport { eventChanges } from \"../../core/projection/events.js\";\nimport { featureStatus } from \"../../core/status/feature/feature.js\";\nimport { executableNext } from \"../../core/status/actions/execution.js\";\nimport { findingJson } from \"../../core/vocabulary/report.js\";\nimport type { DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport { plural } from \"../policy/format.js\";\nimport { mark } from \"./marks.js\";\nimport { errorText } from \"../../core/c4/likec4.js\";\n\nconst REVIEW_INTENT_LIMIT = 1_200;\n\nfunction intentSummary(text: string): string {\n const withoutFrontmatter = text.replace(/^---\\r?\\n[\\s\\S]*?\\r?\\n---\\r?\\n/, \"\");\n const prose = withoutFrontmatter\n .replace(/<!--[\\s\\S]*?-->/g, \"\")\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter((line) => line.length > 0 && !line.startsWith(\"#\"))\n .join(\" \");\n return prose.length <= REVIEW_INTENT_LIMIT\n ? prose\n : `${prose.slice(0, REVIEW_INTENT_LIMIT - 1).trimEnd()}…`;\n}\n\nexport async function showFeature(\n docsDir: DocsDir,\n feature: FeatureEntry,\n json: boolean,\n context = new FleetContext(),\n): Promise<void> {\n const paths = featurePaths(feature.dir);\n const delta = feature.has.delta\n ? await context.loadLikeC4(paths.delta)\n : { errors: [], elements: [] as Elem[], relationships: [] };\n const taggedEls = delta.elements.filter((e) => e.tags.includes(feature.id));\n const taggedRels = delta.relationships.filter((r) => r.tags.includes(feature.id));\n\n const services = [];\n for (const svc of feature.services) {\n const servicePaths = featureSpecPaths(feature.dir, svc);\n const specPath = servicePaths.spec;\n const reqs = existsSync(specPath) ? parseRequirements(await readFile(specPath, \"utf8\")) : [];\n services.push({\n id: svc,\n added: reqs.filter((r) => r.kind === \"ADDED\").length,\n modified: reqs.filter((r) => r.kind === \"MODIFIED\").length,\n removed: reqs.filter((r) => r.kind === \"REMOVED\").length,\n operations: [...new Set(reqs.flatMap((r) => r.operations))],\n api: await apiChanges(servicePaths.openapi),\n events: await eventChanges(servicePaths.asyncapi),\n });\n }\n\n // One `existsSync` for a feature with no capability delta, which is every\n // feature in a fleet that has not adopted the business axis — the walk's own\n // short-circuit, and the whole cost such a fleet pays for this section.\n const capabilities = await capabilityDeltaSummaries(\n (await featureCapabilityDeltas(feature.dir)).docs,\n async (path) => parseRequirements(await readFile(path, \"utf8\")),\n );\n\n if (json) {\n const status = await featureStatus(docsDir, feature, { context });\n const issues = status.checks.issues.map((finding) =>\n findingJson(finding, { path: status.feature.path, role: \"scope\" }),\n );\n const blockers = issues.filter(\n (finding) => finding[\"severity\"] === \"error\" || finding[\"gates\"] === true,\n );\n const intent = existsSync(paths.intent) ? intentSummary(await readFile(paths.intent, \"utf8\")) : \"\";\n emitJson({\n command: \"show\",\n type: \"feature\",\n id: feature.id,\n dirName: feature.dirName,\n path: repoPath(docsDir, feature.dir),\n archived: feature.archived,\n has: feature.has,\n delta: {\n elements: taggedEls.length,\n relationships: taggedRels.length,\n errors: delta.errors.map(errorText),\n },\n services,\n // Additive, which the envelope permits (core/envelope/json.ts), and an\n // EMPTY ARRAY rather than an omitted key for a feature that carries none:\n // a consumer indexing it must not have to tell \"no capability delta\" from\n // \"this loam does not report them\", and the two would be the same absence.\n capabilities: capabilities.map((c) => ({\n id: c.id,\n path: repoPath(docsDir, c.spec),\n added: c.added,\n modified: c.modified,\n removed: c.removed,\n promises: c.promises,\n })),\n review: {\n readyToArchive: !feature.archived && status.feature.stage === \"done\" && blockers.length === 0,\n intent: {\n path: repoPath(docsDir, paths.intent),\n summary: intent,\n },\n architecture: {\n path: repoPath(docsDir, paths.delta),\n elements: taggedEls.map((element) => ({\n id: element.id,\n kind: element.kind,\n title: element.title,\n })),\n relationships: taggedRels.map((relationship) => ({\n source: relationship.source,\n target: relationship.target,\n title: relationship.title ?? null,\n operation: relationship.op ?? null,\n })),\n errors: delta.errors.map(errorText),\n },\n services,\n dependencies: { blockedBy: status.feature.blockedBy },\n artifacts: status.artifacts,\n verification: status.verification,\n blockers,\n advisories: issues.filter(\n (finding) => finding[\"severity\"] === \"warn\" && finding[\"gates\"] !== true,\n ),\n useCases: status.useCases,\n next: status.next.map(executableNext),\n },\n });\n return;\n }\n\n const state = feature.archived ? \"archived\" : \"active\";\n console.log(`${feature.id} ${repoPath(docsDir, feature.dir)} ${state}\\n`);\n\n console.log(\" artifacts\");\n console.log(` ${mark(feature.has.intent)} intent.md`);\n const deltaNote =\n delta.errors.length > 0\n ? `${delta.errors.length} error(s)`\n : `${plural(taggedEls.length, \"element\")} · ${plural(taggedRels.length, \"relationship\")} tagged ${feature.id}`;\n console.log(` ${mark(feature.has.delta)} delta.likec4 ${feature.has.delta ? deltaNote : \"\"}`.trimEnd());\n\n if (services.length > 0) {\n console.log(\"\\n services\");\n const width = Math.max(...services.map((s) => s.id.length));\n for (const s of services) {\n const ops = s.operations.length > 0 ? ` · ${s.operations.join(\", \")}` : \"\";\n console.log(\n ` ${s.id.padEnd(width)} +${s.added} ~${s.modified} -${s.removed} requirements${ops}`,\n );\n }\n }\n\n // Printed only when the feature carries one: a fleet that has not adopted the\n // business axis must see nothing, and an empty \"capabilities\" heading over\n // every feature in every such fleet is exactly the noise that teaches people\n // to stop reading the output.\n if (capabilities.length > 0) {\n console.log(\"\\n capabilities\");\n const width = Math.max(...capabilities.map((c) => c.id.length));\n for (const c of capabilities) {\n // The promise ids, not just the counts: `Realizes: <capability>#<id>` is\n // the line somebody has to write next, and the id is the half of it a\n // reader cannot guess.\n const ids = c.promises.flatMap((p) => (p.id === null ? [] : [p.id]));\n console.log(\n ` ${c.id.padEnd(width)} +${c.added} ~${c.modified} -${c.removed} promises` +\n (ids.length === 0 ? \"\" : ` · ${ids.join(\", \")}`),\n );\n }\n }\n\n if (delta.errors.length > 0) {\n console.log(\"\\n delta errors\");\n for (const e of delta.errors) console.log(` ✗ ${errorText(e)}`);\n }\n}\n\n/* ------------------------------------------------------------------ */\n\n/** \"-\" for absent, matching `list`: ✗ is the error glyph here, and a missing runbook is not an error. */\n"]}
@@ -1,6 +1,7 @@
1
1
  /**
2
- * The four one-line shapes both halves of `loam show` are written in: a tick
3
- * for a file that exists, a scenario count, an edge note, a parse error.
2
+ * The three one-line shapes both halves of `loam show` are written in: a tick
3
+ * for a file that exists, a scenario count, an edge note. (The parse-error
4
+ * line is `errorText` in `core/c4/likec4.ts`, shared with validate.)
4
5
  *
5
6
  * A leaf of its own because the service view and the feature view both use
6
7
  * them, and neither should have to import the other to reach a tick.
@@ -18,7 +19,4 @@ export function edgeNote(e) {
18
19
  return ` ${e.op}`;
19
20
  return e.title ? ` "${e.title}"` : "";
20
21
  }
21
- export function errorText(e) {
22
- return typeof e.line === "number" ? `L${e.line}: ${e.message}` : e.message;
23
- }
24
22
  //# sourceMappingURL=marks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"marks.js","sourceRoot":"","sources":["../../../src/commands/show/marks.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAoB,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAa,MAAM,cAAc,CAAC;AAEzC,MAAM,UAAU,IAAI,CAAC,OAAgB;IACnC,OAAO,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAmB;IAC/C,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,CAAO;IAC9B,IAAI,CAAC,CAAC,EAAE;QAAE,OAAO,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;IAC7B,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,CAAqC;IAC7D,OAAO,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;AAC7E,CAAC","sourcesContent":["/**\n * The four one-line shapes both halves of `loam show` are written in: a tick\n * for a file that exists, a scenario count, an edge note, a parse error.\n *\n * A leaf of its own because the service view and the feature view both use\n * them, and neither should have to import the other to reach a tick.\n */\nimport { type Requirement } from \"../../core/document/spec.js\";\nimport { type Edge } from \"./service.js\";\n\nexport function mark(present: boolean): string {\n return present ? \"✓\" : \"-\";\n}\n\nexport function scenarioCount(reqs: Requirement[]): number {\n return reqs.reduce((n, r) => n + r.scenarios.length, 0);\n}\n\nexport function edgeNote(e: Edge): string {\n if (e.op) return ` ${e.op}`;\n return e.title ? ` \"${e.title}\"` : \"\";\n}\n\nexport function errorText(e: { message: string; line?: number }): string {\n return typeof e.line === \"number\" ? `L${e.line}: ${e.message}` : e.message;\n}\n"]}
1
+ {"version":3,"file":"marks.js","sourceRoot":"","sources":["../../../src/commands/show/marks.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAoB,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAa,MAAM,cAAc,CAAC;AAEzC,MAAM,UAAU,IAAI,CAAC,OAAgB;IACnC,OAAO,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAmB;IAC/C,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,CAAO;IAC9B,IAAI,CAAC,CAAC,EAAE;QAAE,OAAO,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;IAC7B,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;AACzC,CAAC","sourcesContent":["/**\n * The three one-line shapes both halves of `loam show` are written in: a tick\n * for a file that exists, a scenario count, an edge note. (The parse-error\n * line is `errorText` in `core/c4/likec4.ts`, shared with validate.)\n *\n * A leaf of its own because the service view and the feature view both use\n * them, and neither should have to import the other to reach a tick.\n */\nimport { type Requirement } from \"../../core/document/spec.js\";\nimport { type Edge } from \"./service.js\";\n\nexport function mark(present: boolean): string {\n return present ? \"✓\" : \"-\";\n}\n\nexport function scenarioCount(reqs: Requirement[]): number {\n return reqs.reduce((n, r) => n + r.scenarios.length, 0);\n}\n\nexport function edgeNote(e: Edge): string {\n if (e.op) return ` ${e.op}`;\n return e.title ? ` \"${e.title}\"` : \"\";\n}\n"]}
@@ -14,6 +14,7 @@ import { listField, readFrontmatter, stringField } from "../../core/document/fro
14
14
  import { scopeText } from "../../core/provenance/sample/scope.js";
15
15
  import { loadFile } from "../../core/c4/likec4.js";
16
16
  import { serviceResolver } from "../../core/c4/resolve/service.js";
17
+ import { landscapeEvidence } from "../../core/vocabulary/maturity.js";
17
18
  import { readOpenapi } from "../../core/openapi/doc.js";
18
19
  import { landscapePath } from "../../core/repo/paths.js";
19
20
  import { locateServicePaths } from "../../core/repo/service-target.js";
@@ -22,7 +23,8 @@ import { countMarkdown } from "../../core/repo/tree/fs.js";
22
23
  import { parseRequirements } from "../../core/document/parse.js";
23
24
  import {} from "../../core/document/spec.js";
24
25
  import { plural } from "../policy/format.js";
25
- import { edgeNote, errorText, mark, scenarioCount } from "./marks.js";
26
+ import { edgeNote, mark, scenarioCount } from "./marks.js";
27
+ import { errorText } from "../../core/c4/likec4.js";
26
28
  export async function showService(docsDir, id, json, context) {
27
29
  const paths = await locateServicePaths(docsDir, id, context);
28
30
  const has = {
@@ -37,9 +39,29 @@ export async function showService(docsDir, id, json, context) {
37
39
  runbook: existsSync(paths.runbook),
38
40
  health: existsSync(paths.health),
39
41
  };
40
- const model = has.model
41
- ? await loadFile(paths.model)
42
- : { errors: [], elements: [], relationships: [] };
42
+ // Through the ONE reader of `model.likec4`, so the counts here are the counts
43
+ // `validate` prints: a model that EXTENDS the fleet map is a slice of the
44
+ // project it extends, and opening the file alone would show a screenful of
45
+ // parse errors for a document that is perfectly valid where it is read.
46
+ const read = has.model ? await context.serviceModel(docsDir, paths) : null;
47
+ const model = read === null
48
+ ? { errors: [], elements: [], relationships: [] }
49
+ : read.mapUnreadable
50
+ ? // The map did not parse, so an extending model could not be read at
51
+ // all. `show` must say that rather than print zeroes: "0 elements" is
52
+ // what a service with no architecture looks like, and this one may
53
+ // have plenty.
54
+ {
55
+ errors: [
56
+ {
57
+ message: "architecture/landscape.likec4 does not parse, and this model extends it — " +
58
+ "nothing here could be read. `loam validate --all` reports the map's own errors.",
59
+ },
60
+ ],
61
+ elements: [],
62
+ relationships: [],
63
+ }
64
+ : read.doc;
43
65
  const reqs = has.spec ? parseRequirements(await readFile(paths.spec, "utf8")) : [];
44
66
  const archReqs = has.archSpec ? parseRequirements(await readFile(paths.archSpec, "utf8")) : [];
45
67
  const adrs = await countMarkdown(paths.adrsDir);
@@ -209,20 +231,19 @@ export async function landscapeEdges(docsDir, service, context) {
209
231
  // box is titled — otherwise renaming a box empties this list without a word —
210
232
  // and an edge into a modelled container belongs to the service that owns it.
211
233
  const known = new Set((await listServices(docsDir, context)).map((s) => s.id));
212
- const svcOf = serviceResolver(land.elements, known);
213
- const inbound = [];
214
- const outbound = [];
215
- for (const r of land.relationships) {
216
- const edge = (service) => ({
217
- service,
218
- op: r.op ?? null,
219
- title: r.title ?? null,
220
- });
221
- if (svcOf(r.target) === service)
222
- inbound.push(edge(svcOf(r.source)));
223
- else if (svcOf(r.source) === service)
224
- outbound.push(edge(svcOf(r.target)));
225
- }
234
+ // The ONE shared partition (core/vocabulary/maturity.ts `landscapeEvidence`),
235
+ // not a fourth line-for-line copy of it. This loop was that copy, and it kept
236
+ // the rule the others have dropped: an edge whose two endpoints resolve to the
237
+ // same service is its own internal wiring, so `show` printed `← <id>` for a
238
+ // service nobody calls while `context` — which reads the shared derivation —
239
+ // printed nothing. `Edge` is that module's `LandscapeEdge` field for field.
240
+ const { inbound, outbound } = landscapeEvidence({
241
+ id: service,
242
+ parses: true,
243
+ relationships: land.relationships,
244
+ elementIds: land.elements.map((e) => e.id),
245
+ svcOf: serviceResolver(land.elements, known),
246
+ });
226
247
  return { inbound, outbound };
227
248
  }
228
249
  /* ------------------------------------------------------------------ */
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/commands/show/service.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One service as `loam show` presents it: its spec, its contract, and the\n * fleet edges into and out of it.\n *\n * Split from `./feature.ts` because a service and a feature answer different\n * questions — what a repository holds now, against what a change proposes —\n * and only the service view has to reach the landscape for its edges.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { FleetContext } from \"../../core/fleet-context.js\";\nimport { emitJson, repoPath } from \"../../core/envelope/json.js\";\nimport { listField, readFrontmatter, stringField } from \"../../core/document/frontmatter.js\";\nimport { scopeText } from \"../../core/provenance/sample/scope.js\";\nimport { loadFile } from \"../../core/c4/likec4.js\";\nimport { serviceResolver } from \"../../core/c4/resolve/service.js\";\nimport { readOpenapi } from \"../../core/openapi/doc.js\";\nimport type { PathableService } from \"../../core/kernel/ids/service.js\";\nimport { landscapePath } from \"../../core/repo/paths.js\";\nimport { locateServicePaths } from \"../../core/repo/service-target.js\";\nimport { listServices } from \"../../core/repo/repo.js\";\nimport { countMarkdown } from \"../../core/repo/tree/fs.js\";\nimport { parseRequirements } from \"../../core/document/parse.js\";\nimport { type Requirement } from \"../../core/document/spec.js\";\nimport { plural } from \"../policy/format.js\";\nimport { edgeNote, errorText, mark, scenarioCount } from \"./marks.js\";\nimport type { DocsDir } from \"../../core/kernel/ids/dirs.js\";\n\nexport interface Edge {\n service: string;\n op: string | null;\n title: string | null;\n}\n\nexport async function showService(\n docsDir: DocsDir,\n id: PathableService,\n json: boolean,\n context: FleetContext,\n): Promise<void> {\n const paths = await locateServicePaths(docsDir, id, context);\n const has = {\n model: existsSync(paths.model),\n spec: existsSync(paths.spec),\n // The architecture-obligations axis. It was invisible here and in `list`,\n // which made the whole axis unnavigable: a service could carry twenty arch\n // requirements — the outbox, the retries, the alerts — and every command a\n // reader uses to find out what a service documents showed none of them.\n archSpec: existsSync(paths.archSpec),\n openapi: existsSync(paths.openapi),\n runbook: existsSync(paths.runbook),\n health: existsSync(paths.health),\n };\n\n const model = has.model\n ? await loadFile(paths.model)\n : { errors: [], elements: [], relationships: [] };\n const reqs = has.spec ? parseRequirements(await readFile(paths.spec, \"utf8\")) : [];\n const archReqs = has.archSpec ? parseRequirements(await readFile(paths.archSpec, \"utf8\")) : [];\n const adrs = await countMarkdown(paths.adrsDir);\n // `readOpenapi`, not `operationIds`: a contract that exists but does not parse\n // came back from operationIds as an EMPTY operation list, indistinguishable\n // from a service with no endpoints. `show` is what a reader opens to find out\n // what a service offers, and answering \"nothing\" over a YAML error is the\n // worst possible lie for that question. `x-loam-remove` entries are filtered\n // out for the same reason validate filters them: they are deletion markers\n // from a delta, not operations anybody can call.\n const api = await readOpenapi(paths.openapi);\n const ops = api.ops.filter((o) => !o.remove).map((o) => o.id);\n\n // Both axes: the sample is per file, so an arch.spec.md read from a sample\n // beside a fully-read spec.md still means part of this service's\n // documentation was not read, and a badge derived from spec.md alone would\n // not say so.\n const [fm, archFm] = await Promise.all([readFrontmatter(paths.spec), readFrontmatter(paths.archSpec)]);\n const scope = scopeText(fm);\n const archScope = scopeText(archFm);\n const provenance = {\n status: stringField(fm, \"status\") ?? null,\n owner: stringField(fm, \"owner\") ?? null,\n last_verified: stringField(fm, \"last_verified\") ?? null,\n sources: listField(fm, \"sources\"),\n // Additive, under its frontmatter spelling like its siblings: `show` is\n // where a person goes to find out what a service's documents claim, and a\n // `status: verified` that a person reached by reading four sections of the\n // spec is a materially different claim. Verbatim rather than decoded — an\n // unreadable value is still a stamped partial read, and showing it as it\n // stands is what lets a reader see the mangling.\n vouch_scope: scope,\n };\n\n const governs = (op: string): string[] =>\n reqs.filter((r) => r.operations.includes(op)).map((r) => r.name);\n\n const { inbound, outbound } = await landscapeEdges(docsDir, id, context);\n\n const requirementJson = (r: Requirement): Record<string, unknown> => ({\n id: r.id,\n name: r.name,\n kind: r.kind,\n scenarios: r.scenarios.length,\n operations: r.operations,\n covers: r.covers,\n });\n\n if (json) {\n emitJson({\n command: \"show\",\n type: \"service\",\n id,\n path: repoPath(docsDir, paths.dir),\n has,\n adrs,\n frontmatter: provenance,\n model: {\n elements: model.elements.length,\n relationships: model.relationships.length,\n errors: model.errors.map(errorText),\n },\n requirements: reqs.map(requirementJson),\n archSpec: {\n requirements: archReqs.length,\n scenarios: scenarioCount(archReqs),\n entries: archReqs.map(requirementJson),\n // The second axis carries its own scope: one `--sample` run can stamp\n // one of the pair sampled and read the other in full, and the\n // `frontmatter` block above is spec.md's alone.\n vouch_scope: archScope,\n },\n openapi: {\n unreadable: api.unreadable,\n ...(api.error === undefined ? {} : { error: api.error }),\n },\n operations: ops.map((op) => ({ id: op, governedBy: governs(op) })),\n landscape: { inbound, outbound },\n });\n return;\n }\n\n // The badge qualifies the status rather than replacing it: the document\n // still says `verified` and this screen still says so, with what that vouch\n // actually covered attached. A bare `verified` over a sampled stamp is the\n // one reading this whole feature exists to prevent — and either axis earns\n // the qualifier, since either one is part of this service nobody read.\n const shown =\n provenance.status === null || (scope === null && archScope === null)\n ? provenance.status\n : `${provenance.status} (sampled)`;\n const badge = [shown, provenance.owner].filter((s) => s !== null).join(\" · \");\n console.log(`${id} ${repoPath(docsDir, paths.dir)}${badge ? ` ${badge}` : \"\"}\\n`);\n\n if (provenance.sources.length > 0 || provenance.last_verified !== null || archScope !== null) {\n console.log(\" provenance\");\n if (provenance.last_verified !== null) console.log(` verified ${provenance.last_verified}`);\n // Per axis and named, because they can disagree: one file read in full,\n // the other from a sample, is an ordinary outcome of one `--sample` run.\n if (scope !== null) console.log(` scope spec.md ${scope} — the rest of it was not read at that vouch`);\n if (archScope !== null) {\n console.log(` scope arch.spec.md ${archScope} — the rest of it was not read at that vouch`);\n }\n for (const s of provenance.sources) console.log(` sources ${s}`);\n console.log(\"\");\n }\n\n console.log(\" artifacts\");\n const modelNote =\n model.errors.length > 0\n ? `${model.errors.length} error(s)`\n : `${plural(model.elements.length, \"element\")} · ${plural(model.relationships.length, \"relationship\")}`;\n console.log(` ${mark(has.model)} model.likec4 ${has.model ? modelNote : \"\"}`.trimEnd());\n const specNote = (rs: Requirement[]): string =>\n `${plural(rs.length, \"requirement\")} · ${plural(scenarioCount(rs), \"scenario\")}`;\n console.log(` ${mark(has.spec)} spec.md ${has.spec ? specNote(reqs) : \"\"}`.trimEnd());\n console.log(\n ` ${mark(has.archSpec)} arch.spec.md ${has.archSpec ? specNote(archReqs) : \"\"}`.trimEnd(),\n );\n const apiNote = api.unreadable ? \"does not parse\" : plural(ops.length, \"operation\");\n console.log(` ${mark(has.openapi)} openapi.yaml ${has.openapi ? apiNote : \"\"}`.trimEnd());\n console.log(` ${mark(has.runbook)} runbook.md`);\n console.log(` ${mark(has.health)} health.yaml`);\n console.log(` ${mark(adrs > 0)} adrs/ ${adrs > 0 ? plural(adrs, \"decision\") : \"\"}`.trimEnd());\n\n const requirementLine = (r: Requirement): string => {\n const govern = r.operations.length > 0 ? ` → ${r.operations.join(\", \")}` : \"\";\n const covers = r.covers.length > 0 ? ` covers ${r.covers.join(\", \")}` : \"\";\n return ` ${r.name} (${plural(r.scenarios.length, \"scenario\")})${govern}${covers}`;\n };\n if (reqs.length > 0) {\n console.log(\"\\n requirements\");\n for (const r of reqs) console.log(requirementLine(r));\n }\n if (archReqs.length > 0) {\n console.log(\"\\n arch requirements\");\n for (const r of archReqs) console.log(requirementLine(r));\n }\n\n if (api.unreadable) {\n console.log(\"\\n operations\");\n console.log(` ✗ openapi.yaml does not parse — nothing here lists what ${id} offers`);\n if (api.error !== undefined) console.log(` ${api.error}`);\n } else if (ops.length > 0) {\n console.log(\"\\n operations\");\n for (const op of ops) {\n const by = governs(op);\n if (by.length > 0) console.log(` ✓ ${op} governed by ${by.map((n) => `\"${n}\"`).join(\", \")}`);\n else console.log(` ⚠ ${op} not governed by any requirement`);\n }\n }\n\n if (inbound.length > 0 || outbound.length > 0) {\n console.log(\"\\n landscape\");\n for (const e of inbound) console.log(` ← ${e.service}${edgeNote(e)}`);\n for (const e of outbound) console.log(` → ${e.service}${edgeNote(e)}`);\n }\n\n if (model.errors.length > 0) {\n console.log(\"\\n model errors\");\n for (const e of model.errors) console.log(` ✗ ${errorText(e)}`);\n }\n}\n\n/** Inbound/outbound edges for a service, read off the living landscape. */\nexport async function landscapeEdges(\n docsDir: DocsDir,\n service: string,\n context: FleetContext,\n): Promise<{ inbound: Edge[]; outbound: Edge[] }> {\n const path = landscapePath(docsDir);\n if (!existsSync(path)) return { inbound: [], outbound: [] };\n const land = await loadFile(path);\n if (land.errors.length > 0) return { inbound: [], outbound: [] };\n // Edges are filed under the service an element is BOUND to, not under what the\n // box is titled — otherwise renaming a box empties this list without a word —\n // and an edge into a modelled container belongs to the service that owns it.\n const known = new Set((await listServices(docsDir, context)).map((s) => s.id));\n const svcOf = serviceResolver(land.elements, known);\n\n const inbound: Edge[] = [];\n const outbound: Edge[] = [];\n for (const r of land.relationships) {\n const edge = (service: string): Edge => ({\n service,\n op: r.op ?? null,\n title: r.title ?? null,\n });\n if (svcOf(r.target) === service) inbound.push(edge(svcOf(r.source)));\n else if (svcOf(r.source) === service) outbound.push(edge(svcOf(r.target)));\n }\n return { inbound, outbound };\n}\n\n/* ------------------------------------------------------------------ */\n/* Feature */\n/* ------------------------------------------------------------------ */\n"]}
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/commands/show/service.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One service as `loam show` presents it: its spec, its contract, and the\n * fleet edges into and out of it.\n *\n * Split from `./feature.ts` because a service and a feature answer different\n * questions — what a repository holds now, against what a change proposes —\n * and only the service view has to reach the landscape for its edges.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { FleetContext } from \"../../core/fleet-context.js\";\nimport { emitJson, repoPath } from \"../../core/envelope/json.js\";\nimport { listField, readFrontmatter, stringField } from \"../../core/document/frontmatter.js\";\nimport { scopeText } from \"../../core/provenance/sample/scope.js\";\nimport { loadFile, type LoadedDoc } from \"../../core/c4/likec4.js\";\nimport { serviceResolver } from \"../../core/c4/resolve/service.js\";\nimport { landscapeEvidence } from \"../../core/vocabulary/maturity.js\";\nimport { readOpenapi } from \"../../core/openapi/doc.js\";\nimport type { PathableService } from \"../../core/kernel/ids/service.js\";\nimport { landscapePath } from \"../../core/repo/paths.js\";\nimport { locateServicePaths } from \"../../core/repo/service-target.js\";\nimport { listServices } from \"../../core/repo/repo.js\";\nimport { countMarkdown } from \"../../core/repo/tree/fs.js\";\nimport { parseRequirements } from \"../../core/document/parse.js\";\nimport { type Requirement } from \"../../core/document/spec.js\";\nimport { plural } from \"../policy/format.js\";\nimport { edgeNote, mark, scenarioCount } from \"./marks.js\";\nimport { errorText } from \"../../core/c4/likec4.js\";\nimport type { DocsDir } from \"../../core/kernel/ids/dirs.js\";\n\nexport interface Edge {\n service: string;\n op: string | null;\n title: string | null;\n}\n\nexport async function showService(\n docsDir: DocsDir,\n id: PathableService,\n json: boolean,\n context: FleetContext,\n): Promise<void> {\n const paths = await locateServicePaths(docsDir, id, context);\n const has = {\n model: existsSync(paths.model),\n spec: existsSync(paths.spec),\n // The architecture-obligations axis. It was invisible here and in `list`,\n // which made the whole axis unnavigable: a service could carry twenty arch\n // requirements — the outbox, the retries, the alerts — and every command a\n // reader uses to find out what a service documents showed none of them.\n archSpec: existsSync(paths.archSpec),\n openapi: existsSync(paths.openapi),\n runbook: existsSync(paths.runbook),\n health: existsSync(paths.health),\n };\n\n // Through the ONE reader of `model.likec4`, so the counts here are the counts\n // `validate` prints: a model that EXTENDS the fleet map is a slice of the\n // project it extends, and opening the file alone would show a screenful of\n // parse errors for a document that is perfectly valid where it is read.\n const read = has.model ? await context.serviceModel(docsDir, paths) : null;\n const model: LoadedDoc =\n read === null\n ? { errors: [], elements: [], relationships: [] }\n : read.mapUnreadable\n ? // The map did not parse, so an extending model could not be read at\n // all. `show` must say that rather than print zeroes: \"0 elements\" is\n // what a service with no architecture looks like, and this one may\n // have plenty.\n {\n errors: [\n {\n message:\n \"architecture/landscape.likec4 does not parse, and this model extends it — \" +\n \"nothing here could be read. `loam validate --all` reports the map's own errors.\",\n },\n ],\n elements: [],\n relationships: [],\n }\n : read.doc;\n const reqs = has.spec ? parseRequirements(await readFile(paths.spec, \"utf8\")) : [];\n const archReqs = has.archSpec ? parseRequirements(await readFile(paths.archSpec, \"utf8\")) : [];\n const adrs = await countMarkdown(paths.adrsDir);\n // `readOpenapi`, not `operationIds`: a contract that exists but does not parse\n // came back from operationIds as an EMPTY operation list, indistinguishable\n // from a service with no endpoints. `show` is what a reader opens to find out\n // what a service offers, and answering \"nothing\" over a YAML error is the\n // worst possible lie for that question. `x-loam-remove` entries are filtered\n // out for the same reason validate filters them: they are deletion markers\n // from a delta, not operations anybody can call.\n const api = await readOpenapi(paths.openapi);\n const ops = api.ops.filter((o) => !o.remove).map((o) => o.id);\n\n // Both axes: the sample is per file, so an arch.spec.md read from a sample\n // beside a fully-read spec.md still means part of this service's\n // documentation was not read, and a badge derived from spec.md alone would\n // not say so.\n const [fm, archFm] = await Promise.all([readFrontmatter(paths.spec), readFrontmatter(paths.archSpec)]);\n const scope = scopeText(fm);\n const archScope = scopeText(archFm);\n const provenance = {\n status: stringField(fm, \"status\") ?? null,\n owner: stringField(fm, \"owner\") ?? null,\n last_verified: stringField(fm, \"last_verified\") ?? null,\n sources: listField(fm, \"sources\"),\n // Additive, under its frontmatter spelling like its siblings: `show` is\n // where a person goes to find out what a service's documents claim, and a\n // `status: verified` that a person reached by reading four sections of the\n // spec is a materially different claim. Verbatim rather than decoded — an\n // unreadable value is still a stamped partial read, and showing it as it\n // stands is what lets a reader see the mangling.\n vouch_scope: scope,\n };\n\n const governs = (op: string): string[] =>\n reqs.filter((r) => r.operations.includes(op)).map((r) => r.name);\n\n const { inbound, outbound } = await landscapeEdges(docsDir, id, context);\n\n const requirementJson = (r: Requirement): Record<string, unknown> => ({\n id: r.id,\n name: r.name,\n kind: r.kind,\n scenarios: r.scenarios.length,\n operations: r.operations,\n covers: r.covers,\n });\n\n if (json) {\n emitJson({\n command: \"show\",\n type: \"service\",\n id,\n path: repoPath(docsDir, paths.dir),\n has,\n adrs,\n frontmatter: provenance,\n model: {\n elements: model.elements.length,\n relationships: model.relationships.length,\n errors: model.errors.map(errorText),\n },\n requirements: reqs.map(requirementJson),\n archSpec: {\n requirements: archReqs.length,\n scenarios: scenarioCount(archReqs),\n entries: archReqs.map(requirementJson),\n // The second axis carries its own scope: one `--sample` run can stamp\n // one of the pair sampled and read the other in full, and the\n // `frontmatter` block above is spec.md's alone.\n vouch_scope: archScope,\n },\n openapi: {\n unreadable: api.unreadable,\n ...(api.error === undefined ? {} : { error: api.error }),\n },\n operations: ops.map((op) => ({ id: op, governedBy: governs(op) })),\n landscape: { inbound, outbound },\n });\n return;\n }\n\n // The badge qualifies the status rather than replacing it: the document\n // still says `verified` and this screen still says so, with what that vouch\n // actually covered attached. A bare `verified` over a sampled stamp is the\n // one reading this whole feature exists to prevent — and either axis earns\n // the qualifier, since either one is part of this service nobody read.\n const shown =\n provenance.status === null || (scope === null && archScope === null)\n ? provenance.status\n : `${provenance.status} (sampled)`;\n const badge = [shown, provenance.owner].filter((s) => s !== null).join(\" · \");\n console.log(`${id} ${repoPath(docsDir, paths.dir)}${badge ? ` ${badge}` : \"\"}\\n`);\n\n if (provenance.sources.length > 0 || provenance.last_verified !== null || archScope !== null) {\n console.log(\" provenance\");\n if (provenance.last_verified !== null) console.log(` verified ${provenance.last_verified}`);\n // Per axis and named, because they can disagree: one file read in full,\n // the other from a sample, is an ordinary outcome of one `--sample` run.\n if (scope !== null) console.log(` scope spec.md ${scope} — the rest of it was not read at that vouch`);\n if (archScope !== null) {\n console.log(` scope arch.spec.md ${archScope} — the rest of it was not read at that vouch`);\n }\n for (const s of provenance.sources) console.log(` sources ${s}`);\n console.log(\"\");\n }\n\n console.log(\" artifacts\");\n const modelNote =\n model.errors.length > 0\n ? `${model.errors.length} error(s)`\n : `${plural(model.elements.length, \"element\")} · ${plural(model.relationships.length, \"relationship\")}`;\n console.log(` ${mark(has.model)} model.likec4 ${has.model ? modelNote : \"\"}`.trimEnd());\n const specNote = (rs: Requirement[]): string =>\n `${plural(rs.length, \"requirement\")} · ${plural(scenarioCount(rs), \"scenario\")}`;\n console.log(` ${mark(has.spec)} spec.md ${has.spec ? specNote(reqs) : \"\"}`.trimEnd());\n console.log(\n ` ${mark(has.archSpec)} arch.spec.md ${has.archSpec ? specNote(archReqs) : \"\"}`.trimEnd(),\n );\n const apiNote = api.unreadable ? \"does not parse\" : plural(ops.length, \"operation\");\n console.log(` ${mark(has.openapi)} openapi.yaml ${has.openapi ? apiNote : \"\"}`.trimEnd());\n console.log(` ${mark(has.runbook)} runbook.md`);\n console.log(` ${mark(has.health)} health.yaml`);\n console.log(` ${mark(adrs > 0)} adrs/ ${adrs > 0 ? plural(adrs, \"decision\") : \"\"}`.trimEnd());\n\n const requirementLine = (r: Requirement): string => {\n const govern = r.operations.length > 0 ? ` → ${r.operations.join(\", \")}` : \"\";\n const covers = r.covers.length > 0 ? ` covers ${r.covers.join(\", \")}` : \"\";\n return ` ${r.name} (${plural(r.scenarios.length, \"scenario\")})${govern}${covers}`;\n };\n if (reqs.length > 0) {\n console.log(\"\\n requirements\");\n for (const r of reqs) console.log(requirementLine(r));\n }\n if (archReqs.length > 0) {\n console.log(\"\\n arch requirements\");\n for (const r of archReqs) console.log(requirementLine(r));\n }\n\n if (api.unreadable) {\n console.log(\"\\n operations\");\n console.log(` ✗ openapi.yaml does not parse — nothing here lists what ${id} offers`);\n if (api.error !== undefined) console.log(` ${api.error}`);\n } else if (ops.length > 0) {\n console.log(\"\\n operations\");\n for (const op of ops) {\n const by = governs(op);\n if (by.length > 0) console.log(` ✓ ${op} governed by ${by.map((n) => `\"${n}\"`).join(\", \")}`);\n else console.log(` ⚠ ${op} not governed by any requirement`);\n }\n }\n\n if (inbound.length > 0 || outbound.length > 0) {\n console.log(\"\\n landscape\");\n for (const e of inbound) console.log(` ← ${e.service}${edgeNote(e)}`);\n for (const e of outbound) console.log(` → ${e.service}${edgeNote(e)}`);\n }\n\n if (model.errors.length > 0) {\n console.log(\"\\n model errors\");\n for (const e of model.errors) console.log(` ✗ ${errorText(e)}`);\n }\n}\n\n/** Inbound/outbound edges for a service, read off the living landscape. */\nexport async function landscapeEdges(\n docsDir: DocsDir,\n service: string,\n context: FleetContext,\n): Promise<{ inbound: Edge[]; outbound: Edge[] }> {\n const path = landscapePath(docsDir);\n if (!existsSync(path)) return { inbound: [], outbound: [] };\n const land = await loadFile(path);\n if (land.errors.length > 0) return { inbound: [], outbound: [] };\n // Edges are filed under the service an element is BOUND to, not under what the\n // box is titled — otherwise renaming a box empties this list without a word —\n // and an edge into a modelled container belongs to the service that owns it.\n const known = new Set((await listServices(docsDir, context)).map((s) => s.id));\n // The ONE shared partition (core/vocabulary/maturity.ts `landscapeEvidence`),\n // not a fourth line-for-line copy of it. This loop was that copy, and it kept\n // the rule the others have dropped: an edge whose two endpoints resolve to the\n // same service is its own internal wiring, so `show` printed `← <id>` for a\n // service nobody calls while `context` — which reads the shared derivation —\n // printed nothing. `Edge` is that module's `LandscapeEdge` field for field.\n const { inbound, outbound } = landscapeEvidence({\n id: service,\n parses: true,\n relationships: land.relationships,\n elementIds: land.elements.map((e) => e.id),\n svcOf: serviceResolver(land.elements, known),\n });\n return { inbound, outbound };\n}\n\n/* ------------------------------------------------------------------ */\n/* Feature */\n/* ------------------------------------------------------------------ */\n"]}
@@ -98,7 +98,8 @@ export async function runNew(docsDir, name, opts, json) {
98
98
  command: "subsystem",
99
99
  created: name,
100
100
  path: repoPath(docsDir, dir),
101
- views,
101
+ views: views.subsystems,
102
+ serviceViews: views.services,
102
103
  ...(recovered === null ? {} : { recovered }),
103
104
  });
104
105
  return;
@@ -175,7 +176,8 @@ export async function runRm(docsDir, name, json) {
175
176
  removed: name,
176
177
  path: repoPath(docsDir, sub.dir),
177
178
  directoryRemoved,
178
- views,
179
+ views: views.subsystems,
180
+ serviceViews: views.services,
179
181
  ...(recovered === null ? {} : { recovered }),
180
182
  });
181
183
  return;