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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (648) hide show
  1. package/CHANGELOG.md +1315 -1
  2. package/COMPARISON.md +91 -3
  3. package/CONTRIBUTING.md +37 -0
  4. package/MIGRATING-from-OpenSpec.md +40 -22
  5. package/QUICKSTART.md +181 -9
  6. package/README.md +256 -57
  7. package/ROADMAP.md +83 -7
  8. package/SCHEMA.md +569 -98
  9. package/WORKFLOW.md +272 -62
  10. package/dist/cli.js +26 -8
  11. package/dist/cli.js.map +1 -1
  12. package/dist/commands/adopt/adopt.js +5 -10
  13. package/dist/commands/adopt/adopt.js.map +1 -1
  14. package/dist/commands/archive/archive.js +15 -18
  15. package/dist/commands/archive/archive.js.map +1 -1
  16. package/dist/commands/archive/plan/contracts/asyncapi.js +27 -41
  17. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
  18. package/dist/commands/archive/plan/contracts/consumers.js +56 -0
  19. package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
  20. package/dist/commands/archive/plan/contracts/openapi.js +25 -42
  21. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
  22. package/dist/commands/archive/plan/contracts/verdict-lines.js +41 -0
  23. package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
  24. package/dist/commands/archive/plan/gate.js +3 -7
  25. package/dist/commands/archive/plan/gate.js.map +1 -1
  26. package/dist/commands/archive/plan/landscape.js +4 -4
  27. package/dist/commands/archive/plan/landscape.js.map +1 -1
  28. package/dist/commands/archive/plan/refusal.js +8 -18
  29. package/dist/commands/archive/plan/refusal.js.map +1 -1
  30. package/dist/commands/archive/plan/specs.js +134 -25
  31. package/dist/commands/archive/plan/specs.js.map +1 -1
  32. package/dist/commands/archive/plan/state.js +1 -0
  33. package/dist/commands/archive/plan/state.js.map +1 -1
  34. package/dist/commands/archive/run.js +33 -11
  35. package/dist/commands/archive/run.js.map +1 -1
  36. package/dist/commands/archive/shipped.js +49 -0
  37. package/dist/commands/archive/shipped.js.map +1 -0
  38. package/dist/commands/context/context.js +4 -7
  39. package/dist/commands/context/context.js.map +1 -1
  40. package/dist/commands/context/print.js +2 -2
  41. package/dist/commands/context/print.js.map +1 -1
  42. package/dist/commands/delta/delta.js +44 -12
  43. package/dist/commands/delta/delta.js.map +1 -1
  44. package/dist/commands/delta/print.js +1 -1
  45. package/dist/commands/delta/print.js.map +1 -1
  46. package/dist/commands/dependencies.js +3 -7
  47. package/dist/commands/dependencies.js.map +1 -1
  48. package/dist/commands/diff/diff.js +20 -27
  49. package/dist/commands/diff/diff.js.map +1 -1
  50. package/dist/commands/explore.js +4 -8
  51. package/dist/commands/explore.js.map +1 -1
  52. package/dist/commands/gate/gate.js +45 -8
  53. package/dist/commands/gate/gate.js.map +1 -1
  54. package/dist/commands/gate/print.js +15 -3
  55. package/dist/commands/gate/print.js.map +1 -1
  56. package/dist/commands/gherkin/commit.js +13 -54
  57. package/dist/commands/gherkin/commit.js.map +1 -1
  58. package/dist/commands/gherkin/gherkin.js +36 -20
  59. package/dist/commands/gherkin/gherkin.js.map +1 -1
  60. package/dist/commands/gherkin/reconcile.js +10 -13
  61. package/dist/commands/gherkin/reconcile.js.map +1 -1
  62. package/dist/commands/init/first-hour.js +44 -0
  63. package/dist/commands/init/first-hour.js.map +1 -1
  64. package/dist/commands/init/init.js +18 -22
  65. package/dist/commands/init/init.js.map +1 -1
  66. package/dist/commands/list/campaign/campaign.js +4 -6
  67. package/dist/commands/list/campaign/campaign.js.map +1 -1
  68. package/dist/commands/list/json.js +0 -4
  69. package/dist/commands/list/json.js.map +1 -1
  70. package/dist/commands/list/list.js +38 -23
  71. package/dist/commands/list/list.js.map +1 -1
  72. package/dist/commands/list/print.js +38 -6
  73. package/dist/commands/list/print.js.map +1 -1
  74. package/dist/commands/list/suites/suites.js +140 -0
  75. package/dist/commands/list/suites/suites.js.map +1 -0
  76. package/dist/commands/list/views.js +28 -33
  77. package/dist/commands/list/views.js.map +1 -1
  78. package/dist/commands/mcp/dispatch.js +91 -36
  79. package/dist/commands/mcp/dispatch.js.map +1 -1
  80. package/dist/commands/mcp/mcp.js +56 -15
  81. package/dist/commands/mcp/mcp.js.map +1 -1
  82. package/dist/commands/mcp/serve.js.map +1 -1
  83. package/dist/commands/migrate-openspec/materialize/active.js +27 -22
  84. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
  85. package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
  86. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
  87. package/dist/commands/migrate-openspec/materialize/target.js +2 -2
  88. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
  89. package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
  90. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
  91. package/dist/commands/migrate-openspec/openspec/error.js +7 -8
  92. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
  93. package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
  94. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
  95. package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
  96. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
  97. package/dist/commands/new/capability-template.js +118 -0
  98. package/dist/commands/new/capability-template.js.map +1 -0
  99. package/dist/commands/new/commit.js +8 -22
  100. package/dist/commands/new/commit.js.map +1 -1
  101. package/dist/commands/new/modifies.js +206 -0
  102. package/dist/commands/new/modifies.js.map +1 -0
  103. package/dist/commands/new/new.js +59 -14
  104. package/dist/commands/new/new.js.map +1 -1
  105. package/dist/commands/new/templates.js +48 -124
  106. package/dist/commands/new/templates.js.map +1 -1
  107. package/dist/commands/open.js +3 -7
  108. package/dist/commands/open.js.map +1 -1
  109. package/dist/commands/policy/gate.js +27 -0
  110. package/dist/commands/policy/gate.js.map +1 -1
  111. package/dist/commands/rebase/plan.js +2 -2
  112. package/dist/commands/rebase/plan.js.map +1 -1
  113. package/dist/commands/rebase/rebase.js +60 -35
  114. package/dist/commands/rebase/rebase.js.map +1 -1
  115. package/dist/commands/seed/commit.js +21 -25
  116. package/dist/commands/seed/commit.js.map +1 -1
  117. package/dist/commands/seed/seed.js +6 -9
  118. package/dist/commands/seed/seed.js.map +1 -1
  119. package/dist/commands/show/show.js +3 -7
  120. package/dist/commands/show/show.js.map +1 -1
  121. package/dist/commands/status/print.js +13 -4
  122. package/dist/commands/status/print.js.map +1 -1
  123. package/dist/commands/status/status.js +3 -7
  124. package/dist/commands/status/status.js.map +1 -1
  125. package/dist/commands/steps/steps.js +3 -7
  126. package/dist/commands/steps/steps.js.map +1 -1
  127. package/dist/commands/subsystem/edit.js +3 -5
  128. package/dist/commands/subsystem/edit.js.map +1 -1
  129. package/dist/commands/subsystem/move.js +5 -8
  130. package/dist/commands/subsystem/move.js.map +1 -1
  131. package/dist/commands/subsystem/query.js +2 -4
  132. package/dist/commands/subsystem/query.js.map +1 -1
  133. package/dist/commands/subsystem/subsystem.js +4 -7
  134. package/dist/commands/subsystem/subsystem.js.map +1 -1
  135. package/dist/commands/subsystem/sync.js +14 -41
  136. package/dist/commands/subsystem/sync.js.map +1 -1
  137. package/dist/commands/subsystem/txn/txn.js +17 -38
  138. package/dist/commands/subsystem/txn/txn.js.map +1 -1
  139. package/dist/commands/subsystem/txn/views.js +32 -5
  140. package/dist/commands/subsystem/txn/views.js.map +1 -1
  141. package/dist/commands/tasks/render.js +116 -0
  142. package/dist/commands/tasks/render.js.map +1 -0
  143. package/dist/commands/tasks/tasks.js +113 -0
  144. package/dist/commands/tasks/tasks.js.map +1 -0
  145. package/dist/commands/unarchive/manifest.js +6 -9
  146. package/dist/commands/unarchive/manifest.js.map +1 -1
  147. package/dist/commands/unarchive/restore.js +17 -28
  148. package/dist/commands/unarchive/restore.js.map +1 -1
  149. package/dist/commands/unarchive/unarchive.js +22 -30
  150. package/dist/commands/unarchive/unarchive.js.map +1 -1
  151. package/dist/commands/validate/arch-coverage.js +6 -15
  152. package/dist/commands/validate/arch-coverage.js.map +1 -1
  153. package/dist/commands/validate/checks/fleet-shape.js +4 -49
  154. package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
  155. package/dist/commands/validate/checks/requirements.js +18 -8
  156. package/dist/commands/validate/checks/requirements.js.map +1 -1
  157. package/dist/commands/validate/checks/sources.js +6 -2
  158. package/dist/commands/validate/checks/sources.js.map +1 -1
  159. package/dist/commands/validate/feature.js +23 -19
  160. package/dist/commands/validate/feature.js.map +1 -1
  161. package/dist/commands/validate/fleet/landscape.js +23 -23
  162. package/dist/commands/validate/fleet/landscape.js.map +1 -1
  163. package/dist/commands/validate/fleet/map/attest.js +6 -10
  164. package/dist/commands/validate/fleet/map/attest.js.map +1 -1
  165. package/dist/commands/validate/fleet/map/bindings.js +1 -3
  166. package/dist/commands/validate/fleet/map/bindings.js.map +1 -1
  167. package/dist/commands/validate/fleet/map/usecases.js +5 -11
  168. package/dist/commands/validate/fleet/map/usecases.js.map +1 -1
  169. package/dist/commands/validate/fleet/obligations.js +1 -5
  170. package/dist/commands/validate/fleet/obligations.js.map +1 -1
  171. package/dist/commands/validate/fleet/scorecard/print.js +5 -1
  172. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
  173. package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
  174. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
  175. package/dist/commands/validate/fleet/usecases/usecases.js +6 -4
  176. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -1
  177. package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -1
  178. package/dist/commands/validate/fleet/views/root-documents.js +8 -28
  179. package/dist/commands/validate/fleet/views/root-documents.js.map +1 -1
  180. package/dist/commands/validate/links/corpus.js +1 -4
  181. package/dist/commands/validate/links/corpus.js.map +1 -1
  182. package/dist/commands/validate/links/glossary.js.map +1 -1
  183. package/dist/commands/validate/report.js +5 -4
  184. package/dist/commands/validate/report.js.map +1 -1
  185. package/dist/commands/validate/scope.js +113 -0
  186. package/dist/commands/validate/scope.js.map +1 -0
  187. package/dist/commands/validate/service/api.js +16 -104
  188. package/dist/commands/validate/service/api.js.map +1 -1
  189. package/dist/commands/validate/service/contracts/generated.js +134 -0
  190. package/dist/commands/validate/service/contracts/generated.js.map +1 -0
  191. package/dist/commands/validate/service/events/events.js +1 -1
  192. package/dist/commands/validate/service/events/events.js.map +1 -1
  193. package/dist/commands/validate/service/events/message-producers.js +1 -1
  194. package/dist/commands/validate/service/events/message-producers.js.map +1 -1
  195. package/dist/commands/validate/service/evidence-pins.js +56 -10
  196. package/dist/commands/validate/service/evidence-pins.js.map +1 -1
  197. package/dist/commands/validate/service/service.js +16 -26
  198. package/dist/commands/validate/service/service.js.map +1 -1
  199. package/dist/commands/validate/service/specs.js +31 -38
  200. package/dist/commands/validate/service/specs.js.map +1 -1
  201. package/dist/commands/validate/service/spine.js +8 -11
  202. package/dist/commands/validate/service/spine.js.map +1 -1
  203. package/dist/commands/validate/service/usecases/usecases.js +3 -3
  204. package/dist/commands/validate/service/usecases/usecases.js.map +1 -1
  205. package/dist/commands/validate/validate.js +44 -26
  206. package/dist/commands/validate/validate.js.map +1 -1
  207. package/dist/commands/verify/cross/diff.js +2 -6
  208. package/dist/commands/verify/cross/diff.js.map +1 -1
  209. package/dist/commands/verify/evidence/contract.js +1 -1
  210. package/dist/commands/verify/evidence/contract.js.map +1 -1
  211. package/dist/commands/verify/evidence/read.js +12 -3
  212. package/dist/commands/verify/evidence/read.js.map +1 -1
  213. package/dist/commands/verify/frozen.js +45 -23
  214. package/dist/commands/verify/frozen.js.map +1 -1
  215. package/dist/commands/verify/record.js +69 -17
  216. package/dist/commands/verify/record.js.map +1 -1
  217. package/dist/commands/verify/report.js +27 -44
  218. package/dist/commands/verify/report.js.map +1 -1
  219. package/dist/commands/verify/results.js +112 -30
  220. package/dist/commands/verify/results.js.map +1 -1
  221. package/dist/commands/verify/verify.js +54 -51
  222. package/dist/commands/verify/verify.js.map +1 -1
  223. package/dist/commands/vouch/pack/pack.js +2 -2
  224. package/dist/commands/vouch/pack/pack.js.map +1 -1
  225. package/dist/commands/vouch/pack/print.js +1 -6
  226. package/dist/commands/vouch/pack/print.js.map +1 -1
  227. package/dist/commands/vouch/pack/sections.js +1 -2
  228. package/dist/commands/vouch/pack/sections.js.map +1 -1
  229. package/dist/commands/vouch/run.js +26 -81
  230. package/dist/commands/vouch/run.js.map +1 -1
  231. package/dist/commands/vouch/sample/plan.js +20 -34
  232. package/dist/commands/vouch/sample/plan.js.map +1 -1
  233. package/dist/commands/vouch/vet/verify.js +39 -7
  234. package/dist/commands/vouch/vet/verify.js.map +1 -1
  235. package/dist/commands/vouch/vouch.js +10 -17
  236. package/dist/commands/vouch/vouch.js.map +1 -1
  237. package/dist/core/agent/agents-md/command-map.js +56 -101
  238. package/dist/core/agent/agents-md/command-map.js.map +1 -1
  239. package/dist/core/agent/agents-md/map/explain.js +4 -3
  240. package/dist/core/agent/agents-md/map/explain.js.map +1 -1
  241. package/dist/core/agent/agents-md/map/lenses/context.js +4 -3
  242. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
  243. package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
  244. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
  245. package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
  246. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
  247. package/dist/core/agent/agents-md/map/mcp.js +10 -7
  248. package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
  249. package/dist/core/agent/agents-md/map/status.js +9 -3
  250. package/dist/core/agent/agents-md/map/status.js.map +1 -1
  251. package/dist/core/agent/agents-md/map/subsystem.js +4 -3
  252. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
  253. package/dist/core/agent/agents-md/map/usecases.js +24 -6
  254. package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
  255. package/dist/core/agent/agents-md/refusals.js +32 -18
  256. package/dist/core/agent/agents-md/refusals.js.map +1 -1
  257. package/dist/core/agent/agents-md.js +9 -3
  258. package/dist/core/agent/agents-md.js.map +1 -1
  259. package/dist/core/agent/agents-stamp.js +10 -0
  260. package/dist/core/agent/agents-stamp.js.map +1 -1
  261. package/dist/core/agent/scaffold.js +10 -15
  262. package/dist/core/agent/scaffold.js.map +1 -1
  263. package/dist/core/agent/tools/dialects.js +1 -0
  264. package/dist/core/agent/tools/dialects.js.map +1 -1
  265. package/dist/core/agent/tools/registry.js +41 -76
  266. package/dist/core/agent/tools/registry.js.map +1 -1
  267. package/dist/core/agent/workflows/adopt.js +5 -3
  268. package/dist/core/agent/workflows/adopt.js.map +1 -1
  269. package/dist/core/agent/workflows/check.js +20 -16
  270. package/dist/core/agent/workflows/check.js.map +1 -1
  271. package/dist/core/agent/workflows/closing.js +54 -21
  272. package/dist/core/agent/workflows/closing.js.map +1 -1
  273. package/dist/core/agent/workflows/feature.js +79 -11
  274. package/dist/core/agent/workflows/feature.js.map +1 -1
  275. package/dist/core/agent/workflows/implement.js +91 -13
  276. package/dist/core/agent/workflows/implement.js.map +1 -1
  277. package/dist/core/agent/workflows/reference/authoring.js +3 -1
  278. package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
  279. package/dist/core/agent/workflows/reference/codes.js +8 -6
  280. package/dist/core/agent/workflows/reference/codes.js.map +1 -1
  281. package/dist/core/agent/workflows/reference/done-check.js +37 -15
  282. package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
  283. package/dist/core/agent/workflows/reference/reference.js.map +1 -1
  284. package/dist/core/agent/workflows/reference/spine.js +50 -18
  285. package/dist/core/agent/workflows/reference/spine.js.map +1 -1
  286. package/dist/core/asyncapi/baseline/plan.js +14 -81
  287. package/dist/core/asyncapi/baseline/plan.js.map +1 -1
  288. package/dist/core/asyncapi/digest.js +30 -25
  289. package/dist/core/asyncapi/digest.js.map +1 -1
  290. package/dist/core/asyncapi/merge/components.js +5 -4
  291. package/dist/core/asyncapi/merge/components.js.map +1 -1
  292. package/dist/core/asyncapi/merge/error.js +7 -2
  293. package/dist/core/asyncapi/merge/error.js.map +1 -1
  294. package/dist/core/asyncapi/merge/markers.js +30 -50
  295. package/dist/core/asyncapi/merge/markers.js.map +1 -1
  296. package/dist/core/asyncapi/merge/merge.js +31 -53
  297. package/dist/core/asyncapi/merge/merge.js.map +1 -1
  298. package/dist/core/asyncapi/merge/pin.js +10 -36
  299. package/dist/core/asyncapi/merge/pin.js.map +1 -1
  300. package/dist/core/asyncapi/model.js.map +1 -1
  301. package/dist/core/asyncapi/read.js +36 -12
  302. package/dist/core/asyncapi/read.js.map +1 -1
  303. package/dist/core/brief/brief.js +6 -18
  304. package/dist/core/brief/brief.js.map +1 -1
  305. package/dist/core/brief/landscape.js +8 -12
  306. package/dist/core/brief/landscape.js.map +1 -1
  307. package/dist/core/brief/map/extend.js +8 -5
  308. package/dist/core/brief/map/extend.js.map +1 -1
  309. package/dist/core/brief/map/owed.js +8 -26
  310. package/dist/core/brief/map/owed.js.map +1 -1
  311. package/dist/core/brief/targets.js +15 -5
  312. package/dist/core/brief/targets.js.map +1 -1
  313. package/dist/core/c4/likec4.js +147 -16
  314. package/dist/core/c4/likec4.js.map +1 -1
  315. package/dist/core/c4/locate/unlocated.js +150 -0
  316. package/dist/core/c4/locate/unlocated.js.map +1 -0
  317. package/dist/core/c4/parsed/values.js +54 -1
  318. package/dist/core/c4/parsed/values.js.map +1 -1
  319. package/dist/core/c4/prefetch/fleet.js +1 -9
  320. package/dist/core/c4/prefetch/fleet.js.map +1 -1
  321. package/dist/core/c4/project/documents.js +31 -6
  322. package/dist/core/c4/project/documents.js.map +1 -1
  323. package/dist/core/c4/project/load.js +6 -17
  324. package/dist/core/c4/project/load.js.map +1 -1
  325. package/dist/core/c4/resolve/external.js +51 -0
  326. package/dist/core/c4/resolve/external.js.map +1 -0
  327. package/dist/core/c4/resolve/service.js +25 -1
  328. package/dist/core/c4/resolve/service.js.map +1 -1
  329. package/dist/core/c4/seed/template.js +15 -1
  330. package/dist/core/c4/seed/template.js.map +1 -1
  331. package/dist/core/c4/service-model/shape.js +2 -22
  332. package/dist/core/c4/service-model/shape.js.map +1 -1
  333. package/dist/core/c4/source-mask.js +288 -4
  334. package/dist/core/c4/source-mask.js.map +1 -1
  335. package/dist/core/c4/source-scan.js +203 -147
  336. package/dist/core/c4/source-scan.js.map +1 -1
  337. package/dist/core/c4/splice/authored-source.js +19 -0
  338. package/dist/core/c4/splice/authored-source.js.map +1 -1
  339. package/dist/core/c4/splice/delta-blocks.js +5 -18
  340. package/dist/core/c4/splice/delta-blocks.js.map +1 -1
  341. package/dist/core/c4/splice/landscape-merge.js +13 -8
  342. package/dist/core/c4/splice/landscape-merge.js.map +1 -1
  343. package/dist/core/c4/splice/model/merge.js +10 -6
  344. package/dist/core/c4/splice/model/merge.js.map +1 -1
  345. package/dist/core/c4/splice/placement.js +108 -18
  346. package/dist/core/c4/splice/placement.js.map +1 -1
  347. package/dist/core/c4/workspace.js +13 -22
  348. package/dist/core/c4/workspace.js.map +1 -1
  349. package/dist/core/capabilities/capabilities.js +6 -6
  350. package/dist/core/capabilities/capabilities.js.map +1 -1
  351. package/dist/core/capabilities/delta/doc.js +7 -10
  352. package/dist/core/capabilities/delta/doc.js.map +1 -1
  353. package/dist/core/capabilities/findings.js +15 -20
  354. package/dist/core/capabilities/findings.js.map +1 -1
  355. package/dist/core/capabilities/realizes/findings.js +12 -19
  356. package/dist/core/capabilities/realizes/findings.js.map +1 -1
  357. package/dist/core/capabilities/realizes/join.js +0 -20
  358. package/dist/core/capabilities/realizes/join.js.map +1 -1
  359. package/dist/core/capabilities/rollup.js +6 -0
  360. package/dist/core/capabilities/rollup.js.map +1 -1
  361. package/dist/core/capabilities/tree.js +6 -28
  362. package/dist/core/capabilities/tree.js.map +1 -1
  363. package/dist/core/capabilities/usecase-join.js +3 -13
  364. package/dist/core/capabilities/usecase-join.js.map +1 -1
  365. package/dist/core/coherence/authoring/scaffold.js +11 -3
  366. package/dist/core/coherence/authoring/scaffold.js.map +1 -1
  367. package/dist/core/coherence/authoring/sentinels.js +2 -1
  368. package/dist/core/coherence/authoring/sentinels.js.map +1 -1
  369. package/dist/core/coherence/coherence.js +30 -16
  370. package/dist/core/coherence/coherence.js.map +1 -1
  371. package/dist/core/coherence/consumed/operations.js +70 -0
  372. package/dist/core/coherence/consumed/operations.js.map +1 -0
  373. package/dist/core/coherence/consumed/refusal.js +38 -0
  374. package/dist/core/coherence/consumed/refusal.js.map +1 -0
  375. package/dist/core/coherence/declared.js +60 -34
  376. package/dist/core/coherence/declared.js.map +1 -1
  377. package/dist/core/coherence/events/declared.js +44 -59
  378. package/dist/core/coherence/events/declared.js.map +1 -1
  379. package/dist/core/coherence/events/events.js +119 -8
  380. package/dist/core/coherence/events/events.js.map +1 -1
  381. package/dist/core/coherence/events/grades.js +31 -8
  382. package/dist/core/coherence/events/grades.js.map +1 -1
  383. package/dist/core/coherence/events/lookups.js +14 -82
  384. package/dist/core/coherence/events/lookups.js.map +1 -1
  385. package/dist/core/coherence/lookups.js +18 -78
  386. package/dist/core/coherence/lookups.js.map +1 -1
  387. package/dist/core/coherence/retired/archived.js +285 -0
  388. package/dist/core/coherence/retired/archived.js.map +1 -0
  389. package/dist/core/coherence/retired/event-pair.js +62 -0
  390. package/dist/core/coherence/retired/event-pair.js.map +1 -0
  391. package/dist/core/coherence/retired/merged.js +79 -0
  392. package/dist/core/coherence/retired/merged.js.map +1 -0
  393. package/dist/core/coherence/retired/retirement.js +49 -0
  394. package/dist/core/coherence/retired/retirement.js.map +1 -0
  395. package/dist/core/coherence/retired/revival/arrivals.js +131 -0
  396. package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
  397. package/dist/core/coherence/retired/revival/revived.js +297 -0
  398. package/dist/core/coherence/retired/revival/revived.js.map +1 -0
  399. package/dist/core/consumers/after.js +113 -0
  400. package/dist/core/consumers/after.js.map +1 -0
  401. package/dist/core/consumers/consumers.js +296 -0
  402. package/dist/core/consumers/consumers.js.map +1 -0
  403. package/dist/core/consumers/current.js +211 -0
  404. package/dist/core/consumers/current.js.map +1 -0
  405. package/dist/core/consumers/scan.js +46 -0
  406. package/dist/core/consumers/scan.js.map +1 -0
  407. package/dist/core/consumers/state.js +27 -0
  408. package/dist/core/consumers/state.js.map +1 -0
  409. package/dist/core/delta/delta.js +1 -1
  410. package/dist/core/delta/delta.js.map +1 -1
  411. package/dist/core/deployment/delta.js +3 -6
  412. package/dist/core/deployment/delta.js.map +1 -1
  413. package/dist/core/diff/base-git.js +94 -12
  414. package/dist/core/diff/base-git.js.map +1 -1
  415. package/dist/core/diff/base-state.js +28 -16
  416. package/dist/core/diff/base-state.js.map +1 -1
  417. package/dist/core/diff/joins.js +136 -0
  418. package/dist/core/diff/joins.js.map +1 -0
  419. package/dist/core/diff/scope/changed-paths.js +22 -6
  420. package/dist/core/diff/scope/changed-paths.js.map +1 -1
  421. package/dist/core/diff/scope/edges.js +82 -0
  422. package/dist/core/diff/scope/edges.js.map +1 -0
  423. package/dist/core/diff/semantic.js +100 -31
  424. package/dist/core/diff/semantic.js.map +1 -1
  425. package/dist/core/doctor/agents.js +34 -18
  426. package/dist/core/doctor/agents.js.map +1 -1
  427. package/dist/core/doctor/report.js.map +1 -1
  428. package/dist/core/document/apply.js +10 -2
  429. package/dist/core/document/apply.js.map +1 -1
  430. package/dist/core/document/frontmatter.js +55 -0
  431. package/dist/core/document/frontmatter.js.map +1 -1
  432. package/dist/core/document/parse.js +29 -6
  433. package/dist/core/document/parse.js.map +1 -1
  434. package/dist/core/document/scenarios.js +17 -0
  435. package/dist/core/document/scenarios.js.map +1 -1
  436. package/dist/core/document/spec.js +4 -11
  437. package/dist/core/document/spec.js.map +1 -1
  438. package/dist/core/envelope/config.js +10 -0
  439. package/dist/core/envelope/config.js.map +1 -1
  440. package/dist/core/envelope/json.js +34 -0
  441. package/dist/core/envelope/json.js.map +1 -1
  442. package/dist/core/explain/families.js +17 -12
  443. package/dist/core/explain/families.js.map +1 -1
  444. package/dist/core/explain/refusals.js +3 -3
  445. package/dist/core/explain/refusals.js.map +1 -1
  446. package/dist/core/explain/terms.js +18 -9
  447. package/dist/core/explain/terms.js.map +1 -1
  448. package/dist/core/explore/explore.js +2 -7
  449. package/dist/core/explore/explore.js.map +1 -1
  450. package/dist/core/fleet-context.js +34 -95
  451. package/dist/core/fleet-context.js.map +1 -1
  452. package/dist/core/gate/checks.js +5 -2
  453. package/dist/core/gate/checks.js.map +1 -1
  454. package/dist/core/gate/partners.js +4 -2
  455. package/dist/core/gate/partners.js.map +1 -1
  456. package/dist/core/gate/report.js.map +1 -1
  457. package/dist/core/gate/verification.js +75 -12
  458. package/dist/core/gate/verification.js.map +1 -1
  459. package/dist/core/gherkin/stale.js +13 -37
  460. package/dist/core/gherkin/stale.js.map +1 -1
  461. package/dist/core/glossary/tree.js +6 -19
  462. package/dist/core/glossary/tree.js.map +1 -1
  463. package/dist/core/kernel/concurrency.js +19 -0
  464. package/dist/core/kernel/concurrency.js.map +1 -1
  465. package/dist/core/kernel/records.js +11 -0
  466. package/dist/core/kernel/records.js.map +1 -1
  467. package/dist/core/links/backlinks.js +1 -3
  468. package/dist/core/links/backlinks.js.map +1 -1
  469. package/dist/core/links/corpus.js +5 -36
  470. package/dist/core/links/corpus.js.map +1 -1
  471. package/dist/core/mcp/argv.js +101 -0
  472. package/dist/core/mcp/argv.js.map +1 -1
  473. package/dist/core/mcp/author-tools.js +2 -1
  474. package/dist/core/mcp/author-tools.js.map +1 -1
  475. package/dist/core/mcp/protocol.js +2 -2
  476. package/dist/core/mcp/protocol.js.map +1 -1
  477. package/dist/core/mcp/tools.js +43 -33
  478. package/dist/core/mcp/tools.js.map +1 -1
  479. package/dist/core/openapi/baseline/gate.js +53 -12
  480. package/dist/core/openapi/baseline/gate.js.map +1 -1
  481. package/dist/core/openapi/baseline/plan.js +22 -8
  482. package/dist/core/openapi/baseline/plan.js.map +1 -1
  483. package/dist/core/openapi/baseline/record.js +0 -1
  484. package/dist/core/openapi/baseline/record.js.map +1 -1
  485. package/dist/core/openapi/contract-text.js +33 -0
  486. package/dist/core/openapi/contract-text.js.map +1 -0
  487. package/dist/core/openapi/contract-tree.js +56 -0
  488. package/dist/core/openapi/contract-tree.js.map +1 -0
  489. package/dist/core/openapi/depth.js +38 -24
  490. package/dist/core/openapi/depth.js.map +1 -1
  491. package/dist/core/openapi/digest.js +52 -2
  492. package/dist/core/openapi/digest.js.map +1 -1
  493. package/dist/core/openapi/doc.js +12 -4
  494. package/dist/core/openapi/doc.js.map +1 -1
  495. package/dist/core/openapi/merge/components.js +25 -60
  496. package/dist/core/openapi/merge/components.js.map +1 -1
  497. package/dist/core/openapi/merge/error.js +3 -13
  498. package/dist/core/openapi/merge/error.js.map +1 -1
  499. package/dist/core/openapi/merge/markers.js +26 -30
  500. package/dist/core/openapi/merge/markers.js.map +1 -1
  501. package/dist/core/openapi/merge/merge.js +26 -52
  502. package/dist/core/openapi/merge/merge.js.map +1 -1
  503. package/dist/core/openapi/merge/pin.js +15 -35
  504. package/dist/core/openapi/merge/pin.js.map +1 -1
  505. package/dist/core/openspec/decide/verdict.js.map +1 -1
  506. package/dist/core/openspec/inventory.js +4 -5
  507. package/dist/core/openspec/inventory.js.map +1 -1
  508. package/dist/core/openspec/model/mapping.js +30 -28
  509. package/dist/core/openspec/model/mapping.js.map +1 -1
  510. package/dist/core/openspec/model/model.js +12 -7
  511. package/dist/core/openspec/model/model.js.map +1 -1
  512. package/dist/core/openspec/scan/changes.js +10 -4
  513. package/dist/core/openspec/scan/changes.js.map +1 -1
  514. package/dist/core/openspec/scan/config.js +7 -16
  515. package/dist/core/openspec/scan/config.js.map +1 -1
  516. package/dist/core/openspec/scan/shape.js +63 -19
  517. package/dist/core/openspec/scan/shape.js.map +1 -1
  518. package/dist/core/openspec/scan/walk.js +21 -38
  519. package/dist/core/openspec/scan/walk.js.map +1 -1
  520. package/dist/core/pack/living.js +2 -16
  521. package/dist/core/pack/living.js.map +1 -1
  522. package/dist/core/provenance/findings.js +48 -12
  523. package/dist/core/provenance/findings.js.map +1 -1
  524. package/dist/core/provenance/git.js +33 -57
  525. package/dist/core/provenance/git.js.map +1 -1
  526. package/dist/core/provenance/gitq/moves.js +71 -114
  527. package/dist/core/provenance/gitq/moves.js.map +1 -1
  528. package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
  529. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
  530. package/dist/core/provenance/sources.js +23 -6
  531. package/dist/core/provenance/sources.js.map +1 -1
  532. package/dist/core/provenance/stamp.js +5 -17
  533. package/dist/core/provenance/stamp.js.map +1 -1
  534. package/dist/core/repo/entries.js.map +1 -1
  535. package/dist/core/repo/repo.js +24 -14
  536. package/dist/core/repo/repo.js.map +1 -1
  537. package/dist/core/repo/tree/find.js +12 -3
  538. package/dist/core/repo/tree/find.js.map +1 -1
  539. package/dist/core/repo/tree/fs.js +27 -1
  540. package/dist/core/repo/tree/fs.js.map +1 -1
  541. package/dist/core/repo/tree/walk.js +8 -30
  542. package/dist/core/repo/tree/walk.js.map +1 -1
  543. package/dist/core/results.js +47 -12
  544. package/dist/core/results.js.map +1 -1
  545. package/dist/core/staging/commit.js +10 -12
  546. package/dist/core/staging/commit.js.map +1 -1
  547. package/dist/core/staging/lock.js +74 -12
  548. package/dist/core/staging/lock.js.map +1 -1
  549. package/dist/core/staging/recovery/intent.js +2 -5
  550. package/dist/core/staging/recovery/intent.js.map +1 -1
  551. package/dist/core/staging/recovery/recover.js +60 -1
  552. package/dist/core/staging/recovery/recover.js.map +1 -1
  553. package/dist/core/staging/snapshot/manifest.js +152 -0
  554. package/dist/core/staging/snapshot/manifest.js.map +1 -0
  555. package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +10 -34
  556. package/dist/core/staging/snapshot/snapshot.js.map +1 -0
  557. package/dist/core/status/actions/execution.js +11 -0
  558. package/dist/core/status/actions/execution.js.map +1 -1
  559. package/dist/core/status/feature/artifacts.js +2 -2
  560. package/dist/core/status/feature/artifacts.js.map +1 -1
  561. package/dist/core/status/feature/feature.js +5 -11
  562. package/dist/core/status/feature/feature.js.map +1 -1
  563. package/dist/core/status/fleet/fleet.js +6 -2
  564. package/dist/core/status/fleet/fleet.js.map +1 -1
  565. package/dist/core/status/fleet/next.js +26 -3
  566. package/dist/core/status/fleet/next.js.map +1 -1
  567. package/dist/core/status/report.js.map +1 -1
  568. package/dist/core/status/verification.js +30 -15
  569. package/dist/core/status/verification.js.map +1 -1
  570. package/dist/core/tasks/packet.js +174 -0
  571. package/dist/core/tasks/packet.js.map +1 -0
  572. package/dist/core/tasks/record.js +43 -0
  573. package/dist/core/tasks/record.js.map +1 -0
  574. package/dist/core/tasks/sources.js +181 -0
  575. package/dist/core/tasks/sources.js.map +1 -0
  576. package/dist/core/tasks/stamps.js +76 -0
  577. package/dist/core/tasks/stamps.js.map +1 -0
  578. package/dist/core/tasks/unit.js +0 -0
  579. package/dist/core/tasks/unit.js.map +1 -0
  580. package/dist/core/usecases/capability.js +11 -14
  581. package/dist/core/usecases/capability.js.map +1 -1
  582. package/dist/core/usecases/delta/flows.js +5 -7
  583. package/dist/core/usecases/delta/flows.js.map +1 -1
  584. package/dist/core/usecases/fleet.js +178 -31
  585. package/dist/core/usecases/fleet.js.map +1 -1
  586. package/dist/core/usecases/operations.js +5 -4
  587. package/dist/core/usecases/operations.js.map +1 -1
  588. package/dist/core/usecases/service/flows.js +9 -6
  589. package/dist/core/usecases/service/flows.js.map +1 -1
  590. package/dist/core/usecases/suites/participants.js +83 -0
  591. package/dist/core/usecases/suites/participants.js.map +1 -0
  592. package/dist/core/usecases/suites/suites.js +45 -0
  593. package/dist/core/usecases/suites/suites.js.map +1 -0
  594. package/dist/core/verify/answers.js +12 -5
  595. package/dist/core/verify/answers.js.map +1 -1
  596. package/dist/core/verify/build.js +102 -82
  597. package/dist/core/verify/build.js.map +1 -1
  598. package/dist/core/verify/checklist.js +3 -0
  599. package/dist/core/verify/checklist.js.map +1 -1
  600. package/dist/core/verify/location.js +67 -0
  601. package/dist/core/verify/location.js.map +1 -0
  602. package/dist/core/verify/pins/lint.js +3 -2
  603. package/dist/core/verify/pins/lint.js.map +1 -1
  604. package/dist/core/verify/pins/pin.js +1 -1
  605. package/dist/core/verify/pins/pin.js.map +1 -1
  606. package/dist/core/verify/read/file.js +224 -0
  607. package/dist/core/verify/read/file.js.map +1 -0
  608. package/dist/core/verify/{file.js → read/shape.js} +16 -104
  609. package/dist/core/verify/read/shape.js.map +1 -0
  610. package/dist/core/verify/read/union.js +105 -0
  611. package/dist/core/verify/read/union.js.map +1 -0
  612. package/dist/core/verify/record.js +35 -12
  613. package/dist/core/verify/record.js.map +1 -1
  614. package/dist/core/verify/store/commit.js +40 -30
  615. package/dist/core/verify/store/commit.js.map +1 -1
  616. package/dist/core/verify/store/render.js +8 -4
  617. package/dist/core/verify/store/render.js.map +1 -1
  618. package/dist/core/vocabulary/issue.js +1 -0
  619. package/dist/core/vocabulary/issue.js.map +1 -1
  620. package/dist/core/vocabulary/maturity.js +12 -2
  621. package/dist/core/vocabulary/maturity.js.map +1 -1
  622. package/examples/README.md +21 -6
  623. package/examples/docs/architecture/landscape.likec4 +8 -0
  624. package/examples/docs/architecture/usecases/order-notification.likec4 +6 -2
  625. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
  626. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
  627. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
  628. package/examples/docs/services/order-service/asyncapi.yaml +3 -2
  629. package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
  630. package/package.json +1 -1
  631. package/dist/commands/validate/checks/vocabulary.js +0 -16
  632. package/dist/commands/validate/checks/vocabulary.js.map +0 -1
  633. package/dist/core/agent/agents-md/artifacts.js +0 -221
  634. package/dist/core/agent/agents-md/artifacts.js.map +0 -1
  635. package/dist/core/agent/agents-md/cycle.js +0 -180
  636. package/dist/core/agent/agents-md/cycle.js.map +0 -1
  637. package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
  638. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
  639. package/dist/core/agent/agents-md/spine.js +0 -40
  640. package/dist/core/agent/agents-md/spine.js.map +0 -1
  641. package/dist/core/coherence/events/merged.js +0 -40
  642. package/dist/core/coherence/events/merged.js.map +0 -1
  643. package/dist/core/diff/current-state.js +0 -96
  644. package/dist/core/diff/current-state.js.map +0 -1
  645. package/dist/core/diff/victims.js +0 -256
  646. package/dist/core/diff/victims.js.map +0 -1
  647. package/dist/core/staging/snapshot.js.map +0 -1
  648. package/dist/core/verify/file.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"commit.js","sourceRoot":"","sources":["../../../src/commands/seed/commit.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Seed's commit window: lock, recover, the under-lock provenance and\n * fleet-consistency checks, one journaled transaction. Modeled line for line\n * on commands/new/commit.ts and on the same seam: everything in here holds\n * the docs lock, nothing outside it does, and the module prints nothing —\n * the caller renders every arm.\n *\n * The three refusals live UNDER the lock on purpose: the never-overwrite promise is a\n * statement about the landscape as it is at the moment of the write, and an\n * unlocked classification would be a TOCTOU against every other docs writer.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport type { FleetSeed } from \"../../core/c4/seed/fleet-file.js\";\nimport { landscapeProvenance } from \"../../core/c4/seed/stamp.js\";\nimport { isLandscapeStub } from \"../../core/scaffold/landscape.js\";\nimport type { DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport { parseServiceId } from \"../../core/kernel/ids/service.js\";\nimport { landscapePath } from \"../../core/repo/paths.js\";\nimport { listFleetTree } from \"../../core/repo/repo.js\";\nimport { authoredRefusal } from \"../../core/repo/tree/render/service/header.js\";\nimport { DocsRepoUnavailableError } from \"../../core/repo/state.js\";\nimport type { FleetTree } from \"../../core/repo/tree/walk.js\";\nimport { stageWrites } from \"../../core/staging/commit.js\";\nimport { type CommitRecovery, InterruptedCommitError } from \"../../core/staging/interrupted.js\";\nimport { acquireDocsLockWaiting, DocsBusyError, LOCK_WAIT_MS } from \"../../core/staging/lock.js\";\nimport { recoverInterruptedCommit } from \"../../core/staging/recovery/recover.js\";\nimport { commitStaged } from \"../../core/staging/txn/transaction.js\";\nimport { planSeedWrites, type SeedPlanInput } from \"./plan.js\";\n\n/** The plan's input, plus the one fact only the transaction needs. */\nexport interface SeedCommitRequest extends SeedPlanInput {\n /** How a human re-runs this command — the transaction journal's `rerun`. */\n rerun: string;\n}\n\n/** What happened to architecture/landscape.likec4 — the three writes seed may prove safe. */\nexport type LandscapeDisposition = \"created\" | \"replaced-stub\" | \"regenerated\";\n\nexport type SeedCommit =\n | {\n ok: true;\n landscape: LandscapeDisposition;\n /** Absolute path of every file the transaction wrote, in write order. */\n written: string[];\n /**\n * Absolute path of every file the transaction REMOVED — only ever the\n * generated views file, when the resulting tree has no subsystems left\n * to view. Separate from `written` because \"+ path\" is a lie about a\n * delete.\n */\n removed: string[];\n /** Service ids split by whether this run created the directory. */\n services: { created: string[]; existing: string[] };\n recovered: CommitRecovery | null;\n }\n | {\n ok: false;\n code:\n | \"docs-busy\"\n | \"commit-interrupted\"\n | \"merge-failed\"\n | \"rollback-incomplete\"\n | \"seed-landscape-edited\"\n /** A hand-authored document standing at one of the two generated views paths — see the refusal in `commitSeed`. */\n | \"already-exists\";\n message: string;\n }\n /** The under-lock fleet-consistency preflight refusing; see `missingServices`. */\n | { ok: false; code: \"seed-file-invalid\"; message: string; missingServices: string[] }\n /** The docs repo went away between the command's gate and the locked read — new/commit.ts says why this is not a `code:` arm. */\n | { ok: false; repoGone: DocsRepoUnavailableError };\n\nexport async function commitSeed(req: SeedCommitRequest): Promise<SeedCommit> {\n const { docsDir } = req;\n let releaseLock: () => Promise<void>;\n try {\n releaseLock = await acquireDocsLockWaiting(docsDir, LOCK_WAIT_MS);\n } catch (err) {\n if (err instanceof DocsBusyError) return { ok: false, code: \"docs-busy\", message: err.message };\n throw err;\n }\n try {\n let recovered: CommitRecovery | null;\n try {\n recovered = await recoverInterruptedCommit(docsDir);\n } catch (err) {\n if (!(err instanceof InterruptedCommitError)) throw err;\n return { ok: false, code: \"commit-interrupted\", message: err.message };\n }\n\n // Provenance first, then the tree: both read the repo as this run will\n // leave it, and both refusals must prove nothing was written.\n const landscape = await classifyLandscape(docsDir);\n if (\"refusal\" in landscape) {\n return { ok: false, code: \"seed-landscape-edited\", message: landscape.refusal };\n }\n\n let tree: FleetTree;\n try {\n tree = await listFleetTree(docsDir);\n } catch (err) {\n if (!(err instanceof DocsRepoUnavailableError)) throw err;\n return { ok: false, repoGone: err };\n }\n const inconsistency = preflight(tree, req.seed);\n if (inconsistency !== null) return inconsistency;\n\n const plan = await planSeedWrites(req, tree);\n // THE OTHER never-overwrite promise, and the third refusal this window owns.\n //\n // `classifyLandscape` above keeps it for `architecture/landscape.likec4`;\n // this keeps it for the two files seed does not share with the team at all —\n // `architecture/subsystems.likec4` and `architecture/services.likec4` are\n // filenames loam RESERVES inside a directory the team owns, and seed writes\n // them whole. Until this line existed a `loam seed` into a repository that\n // kept a hand-authored diagram at one of those paths destroyed it in\n // silence: the same clobber reproduced through `loam subsystem new`, and the\n // guard that caught it lived in `subsystem sync` alone.\n //\n // THE WHOLE SEED IS REFUSED, exactly as an edited landscape refuses it, and\n // for the same reason: seed regenerates every file it owns wholesale from\n // fleet.yaml, so landing the landscape and the markers while declining a\n // views file would leave a tree whose very next `validate --all` — seed's\n // own `next` command — reports `subsystem.views-stale` over a file no\n // command could ever repair. `planSeedWrites` has staged nothing; the\n // transaction below is the first byte, and it does not run.\n if (plan.authored.length > 0) {\n return {\n ok: false,\n // `already-exists` for the reason commands/subsystem/txn/txn.ts gives at\n // its own copy of this refusal: it is what that code has always meant —\n // refusing to create over something that already exists, because\n // overwriting would destroy work nobody asked to lose.\n code: \"already-exists\",\n message:\n plan.authored.map((file) => authoredRefusal(file)).join(\" \") +\n \" NOTHING was written: not the landscape, not a subsystem marker, not a `.gitkeep`. Seed regenerates \" +\n \"every file it owns wholesale, so it never proceeds past human work standing at one of its paths — the \" +\n \"same promise `seed-landscape-edited` makes one file over.\",\n };\n }\n const staged = await stageWrites(plan.writes);\n const committed = await commitStaged(\n { root: docsDir, command: \"seed\", rerun: req.rerun, target: \"landscape\" },\n staged,\n \"seeded\",\n );\n if (!committed.ok) {\n // `committed.raced` is deliberately NOT remapped: the lock serialises\n // loam writers, so a fired CAS or a lost exclusive create here means an\n // out-of-band editor, and seed has no softer race answer the way `new`\n // has already-exists — merge-failed's \"nothing was written, re-run\" is\n // the honest sentence either way.\n return { ok: false, code: committed.code, message: committed.message };\n }\n return {\n ok: true,\n landscape: landscape.disposition,\n written: plan.writes.filter((w) => w.content !== null).map((w) => w.path),\n removed: plan.writes.filter((w) => w.content === null).map((w) => w.path),\n services: plan.services,\n recovered,\n };\n } finally {\n await releaseLock();\n }\n}\n\n/**\n * May seed write the landscape? Absent and untouched-scaffold-stub are the\n * two states with provably no human work in them; a matching stamp digest is\n * the third (`core/c4/seed/stamp.ts`). Everything else refuses.\n */\nasync function classifyLandscape(\n docsDir: DocsDir,\n): Promise<{ disposition: LandscapeDisposition } | { refusal: string }> {\n const path = landscapePath(docsDir);\n if (!existsSync(path)) return { disposition: \"created\" };\n const text = (await readFile(path)).toString(\"utf8\");\n // `isLandscapeStub`, never a byte-compare of our own: that function IS the\n // repo's answer to \"is this still the scaffold's untouched map\", it is what\n // `loam status` reads before telling the reader to run seed, and it is\n // suffix-matched and CRLF-normalised for two reasons seed shares —\n // `migrate-openspec` prepends a preamble to the identical stub, and a\n // checkout under `core.autocrlf` (Git for Windows' installer default)\n // rewrites every line without touching a fact. A stricter compare here made\n // status and seed contradict each other about the same file.\n if (isLandscapeStub(text)) return { disposition: \"replaced-stub\" };\n const provenance = landscapeProvenance(text);\n if (provenance === \"seed-stamped\") return { disposition: \"regenerated\" };\n return {\n refusal:\n `architecture/landscape.likec4 ${\n provenance === \"seed-edited\"\n ? \"was seeded but has been hand-edited since — the line-1 stamp's digest no longer matches the content\"\n : \"was not written by `loam seed`: it carries no line-1 stamp and is not the scaffold's untouched stub\"\n }. Seed never overwrites human work; nothing was written. Either fold the edits into ` +\n `fleet.yaml and delete the file, then re-run — or keep the hand-authored map and stop ` +\n `using seed here.`,\n };\n}\n\n/**\n * The fleet-consistency preflight. Seed regenerates the landscape WHOLESALE —\n * that is the only posture the stamp can prove safe — so every existing\n * services/<id>/ must be named in fleet.yaml, or the regenerate would orphan\n * it into `landscape.service-unmodelled`. And the file's names must not\n * collide with what the tree already holds under a different kind: one flat\n * namespace, the same rule the walk enforces.\n */\nfunction preflight(tree: FleetTree, seed: FleetSeed): SeedCommit | null {\n const treeSubs = new Set(tree.subsystems.map((s) => s.name));\n const treeIds = new Set(tree.services.map((s) => s.id as string));\n\n // The collision scan runs BEFORE the missing-service scan, and the order is\n // load-bearing. A name the file declares as a subsystem or an external while\n // the tree holds it as a service DIRECTORY is both facts at once: it is a\n // namespace collision, and it is also an existing service the file does not\n // name. With the missing scan first, the caller was told to paste that name\n // into `services:` — advice that walks straight into `seed-duplicate-service`\n // on the next run, because the file already declares it as something else.\n const collisions: string[] = [];\n for (const sub of seed.subsystems) {\n if (treeIds.has(sub as string)) collisions.push(`subsystem '${sub}' is an existing service directory`);\n }\n for (const s of seed.services) {\n if (treeSubs.has(s.id as string)) collisions.push(`service '${s.id}' is an existing subsystem directory`);\n }\n for (const x of seed.externals) {\n if (treeIds.has(x)) collisions.push(`external '${x}' is an existing service directory`);\n if (treeSubs.has(x)) collisions.push(`external '${x}' is an existing subsystem directory`);\n }\n if (collisions.length > 0) {\n return {\n ok: false,\n code: \"seed-file-invalid\",\n message:\n `fleet.yaml collides with the services/ tree — service ids, subsystem names and externals ` +\n `share one flat namespace: ${collisions.join(\"; \")}. Rename the fleet.yaml side (or the ` +\n `directory, with \\`loam subsystem rename <from> <to>\\`), then re-run. Nothing was written.`,\n missingServices: [],\n };\n }\n\n const ids = new Set(seed.services.map((s) => s.id as string));\n const missing = tree.services.map((s) => s.id as string).filter((id) => !ids.has(id)).sort();\n if (missing.length === 0) return null;\n // The ids are partitioned because one half cannot take the other's advice.\n // `tree.services` carries RAW ids — a directory whose name fails the service\n // grammar is still a service to the walk, by design — and \"add it to\n // services: exactly as spelled\" is impossible for one of those: the fleet\n // file would refuse the entry. Without this split, seed was simply unusable\n // on such a repo and never said so.\n const legal = missing.filter((id) => parseServiceId(id, \"services entry\").ok);\n const illegal = missing.filter((id) => !parseServiceId(id, \"services entry\").ok);\n const fixes: string[] = [];\n if (legal.length > 0) fixes.push(`add ${legal.join(\", \")} to services:, exactly as spelled`);\n if (illegal.length > 0) {\n fixes.push(\n `rename the ${illegal.length === 1 ? \"directory\" : \"directories\"} ${illegal.join(\", \")} ` +\n `first — ${illegal.length === 1 ? \"that name is not a legal service id\" : \"those names are not legal service ids\"}, ` +\n `so no services: entry can name ${illegal.length === 1 ? \"it\" : \"them\"}`,\n );\n }\n return {\n ok: false,\n code: \"seed-file-invalid\",\n message:\n `fleet.yaml must name every existing service — seed regenerates the landscape wholesale, ` +\n `and these services/ directories are not in it: ${missing.join(\", \")}. ` +\n `To fix: ${fixes.join(\"; and \")}. Then re-run. Nothing was written.`,\n missingServices: missing,\n };\n}\n"]}
1
+ {"version":3,"file":"commit.js","sourceRoot":"","sources":["../../../src/commands/seed/commit.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Seed's commit window: lock, recover, the under-lock provenance and\n * fleet-consistency checks, one journaled transaction. Modeled line for line\n * on commands/new/commit.ts and on the same seam: everything in here holds\n * the docs lock, nothing outside it does, and the module prints nothing —\n * the caller renders every arm.\n *\n * The three refusals live UNDER the lock on purpose: the never-overwrite promise is a\n * statement about the landscape as it is at the moment of the write, and an\n * unlocked classification would be a TOCTOU against every other docs writer.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport type { FleetSeed } from \"../../core/c4/seed/fleet-file.js\";\nimport { landscapeProvenance } from \"../../core/c4/seed/stamp.js\";\nimport { isLandscapeStub } from \"../../core/scaffold/landscape.js\";\nimport type { DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport { parseServiceId } from \"../../core/kernel/ids/service.js\";\nimport { landscapePath } from \"../../core/repo/paths.js\";\nimport { listFleetTree } from \"../../core/repo/repo.js\";\nimport { authoredRefusal } from \"../../core/repo/tree/render/service/header.js\";\nimport { DocsRepoUnavailableError } from \"../../core/repo/state.js\";\nimport type { FleetTree } from \"../../core/repo/tree/walk.js\";\nimport { stageWrites } from \"../../core/staging/commit.js\";\nimport { type CommitRecovery } from \"../../core/staging/interrupted.js\";\nimport { LOCK_WAIT_MS } from \"../../core/staging/lock.js\";\nimport { openCommitWindow } from \"../../core/staging/recovery/recover.js\";\nimport { commitStaged } from \"../../core/staging/txn/transaction.js\";\nimport { planSeedWrites, type SeedPlanInput } from \"./plan.js\";\n\n/** The plan's input, plus the one fact only the transaction needs. */\nexport interface SeedCommitRequest extends SeedPlanInput {\n /** How a human re-runs this command — the transaction journal's `rerun`. */\n rerun: string;\n}\n\n/** What happened to architecture/landscape.likec4 — the three writes seed may prove safe. */\nexport type LandscapeDisposition = \"created\" | \"replaced-stub\" | \"regenerated\";\n\nexport type SeedCommit =\n | {\n ok: true;\n landscape: LandscapeDisposition;\n /** Absolute path of every file the transaction wrote, in write order. */\n written: string[];\n /**\n * Absolute path of every file the transaction REMOVED — only ever the\n * generated views file, when the resulting tree has no subsystems left\n * to view. Separate from `written` because \"+ path\" is a lie about a\n * delete.\n */\n removed: string[];\n /** Service ids split by whether this run created the directory. */\n services: { created: string[]; existing: string[] };\n recovered: CommitRecovery | null;\n }\n | {\n ok: false;\n code:\n | \"docs-busy\"\n | \"commit-interrupted\"\n | \"merge-failed\"\n | \"rollback-incomplete\"\n | \"seed-landscape-edited\"\n /** A hand-authored document standing at one of the two generated views paths — see the refusal in `commitSeed`. */\n | \"already-exists\";\n message: string;\n }\n /** The under-lock fleet-consistency preflight refusing; see `missingServices`. */\n | { ok: false; code: \"seed-file-invalid\"; message: string; missingServices: string[] }\n /** The docs repo went away between the command's gate and the locked read — new/commit.ts says why this is not a `code:` arm. */\n | { ok: false; repoGone: DocsRepoUnavailableError };\n\nexport async function commitSeed(req: SeedCommitRequest): Promise<SeedCommit> {\n const { docsDir } = req;\n const opened = await openCommitWindow(docsDir, LOCK_WAIT_MS);\n if (!opened.ok) return opened;\n const { recovered } = opened;\n try {\n // Provenance first, then the tree: both read the repo as this run will\n // leave it, and both refusals must prove nothing was written.\n const landscape = await classifyLandscape(docsDir);\n if (\"refusal\" in landscape) {\n return { ok: false, code: \"seed-landscape-edited\", message: landscape.refusal };\n }\n\n let tree: FleetTree;\n try {\n tree = await listFleetTree(docsDir);\n } catch (err) {\n if (!(err instanceof DocsRepoUnavailableError)) throw err;\n return { ok: false, repoGone: err };\n }\n const inconsistency = preflight(tree, req.seed);\n if (inconsistency !== null) return inconsistency;\n\n const plan = await planSeedWrites(req, tree);\n // THE OTHER never-overwrite promise, and the third refusal this window owns.\n //\n // `classifyLandscape` above keeps it for `architecture/landscape.likec4`;\n // this keeps it for the two files seed does not share with the team at all —\n // `architecture/subsystems.likec4` and `architecture/services.likec4` are\n // filenames loam RESERVES inside a directory the team owns, and seed writes\n // them whole. Until this line existed a `loam seed` into a repository that\n // kept a hand-authored diagram at one of those paths destroyed it in\n // silence: the same clobber reproduced through `loam subsystem new`, and the\n // guard that caught it lived in `subsystem sync` alone.\n //\n // THE WHOLE SEED IS REFUSED, exactly as an edited landscape refuses it, and\n // for the same reason: seed regenerates every file it owns wholesale from\n // fleet.yaml, so landing the landscape and the markers while declining a\n // views file would leave a tree whose very next `validate --all` — seed's\n // own `next` command — reports `subsystem.views-stale` over a file no\n // command could ever repair. `planSeedWrites` has staged nothing; the\n // transaction below is the first byte, and it does not run.\n if (plan.authored.length > 0) {\n return {\n ok: false,\n // `already-exists` for the reason commands/subsystem/txn/txn.ts gives at\n // its own copy of this refusal: it is what that code has always meant —\n // refusing to create over something that already exists, because\n // overwriting would destroy work nobody asked to lose.\n code: \"already-exists\",\n message:\n plan.authored.map((file) => authoredRefusal(file)).join(\" \") +\n \" NOTHING was written: not the landscape, not a subsystem marker, not a `.gitkeep`. Seed regenerates \" +\n \"every file it owns wholesale, so it never proceeds past human work standing at one of its paths — the \" +\n \"same promise `seed-landscape-edited` makes one file over.\",\n };\n }\n const staged = await stageWrites(plan.writes);\n const committed = await commitStaged(\n { root: docsDir, command: \"seed\", rerun: req.rerun, target: \"landscape\" },\n staged,\n \"seeded\",\n );\n if (!committed.ok) {\n // `committed.raced` is deliberately NOT remapped: the lock serialises\n // loam writers, so a fired CAS or a lost exclusive create here means an\n // out-of-band editor, and seed has no softer race answer the way `new`\n // has already-exists — merge-failed's \"nothing was written, re-run\" is\n // the honest sentence either way.\n return { ok: false, code: committed.code, message: committed.message };\n }\n return {\n ok: true,\n landscape: landscape.disposition,\n written: plan.writes.filter((w) => w.content !== null).map((w) => w.path),\n removed: plan.writes.filter((w) => w.content === null).map((w) => w.path),\n services: plan.services,\n recovered,\n };\n } finally {\n await opened.release();\n }\n}\n\n/**\n * May seed write the landscape? Absent and untouched-scaffold-stub are the\n * two states with provably no human work in them; a matching stamp digest is\n * the third (`core/c4/seed/stamp.ts`). Everything else refuses.\n */\nasync function classifyLandscape(\n docsDir: DocsDir,\n): Promise<{ disposition: LandscapeDisposition } | { refusal: string }> {\n const path = landscapePath(docsDir);\n if (!existsSync(path)) return { disposition: \"created\" };\n const text = (await readFile(path)).toString(\"utf8\");\n // `isLandscapeStub`, never a byte-compare of our own: that function IS the\n // repo's answer to \"is this still the scaffold's untouched map\", it is what\n // `loam status` reads before telling the reader to run seed, and it is\n // suffix-matched and CRLF-normalised for two reasons seed shares —\n // `migrate-openspec` prepends a preamble to the identical stub, and a\n // checkout under `core.autocrlf` (Git for Windows' installer default)\n // rewrites every line without touching a fact. A stricter compare here made\n // status and seed contradict each other about the same file.\n if (isLandscapeStub(text)) return { disposition: \"replaced-stub\" };\n const provenance = landscapeProvenance(text);\n if (provenance === \"seed-stamped\") return { disposition: \"regenerated\" };\n // The advice used to be \"fold the edits into fleet.yaml and delete the\n // file\", which is impossible for the edit every documented next step makes:\n // the first `metadata { op }` an author or an adopt brief writes onto an edge\n // has no spelling in fleet.yaml (services, subsystems, plain `a -> b` calls),\n // so a reader following it would delete the operation links the spine is\n // graded on. Seed is a day-zero tool; the honest answer names what grows the\n // map after day zero.\n return {\n refusal:\n `architecture/landscape.likec4 ${\n provenance === \"seed-edited\"\n ? \"was seeded but has been hand-edited since — the line-1 stamp's digest no longer matches the content\"\n : \"was not written by `loam seed`: it carries no line-1 stamp and is not the scaffold's untouched stub\"\n }. Seed never overwrites human work; nothing was written. Seed runs once, before anyone ` +\n `draws an operation: fleet.yaml cannot carry \\`metadata { op }\\`, publishes or consumes, so a map ` +\n `holding them is past seeding. Grow it from here without seed — \\`loam adopt --service <id>\\` ` +\n `briefs the element and edges a new service adds, and a feature's C4 delta carries every other ` +\n `change. Only if the edits are things fleet.yaml can say (a service, a subsystem, a plain call) ` +\n `is folding them into fleet.yaml and deleting the file still an option.`,\n };\n}\n\n/**\n * The fleet-consistency preflight. Seed regenerates the landscape WHOLESALE —\n * that is the only posture the stamp can prove safe — so every existing\n * services/<id>/ must be named in fleet.yaml, or the regenerate would orphan\n * it into `landscape.service-unmodelled`. And the file's names must not\n * collide with what the tree already holds under a different kind: one flat\n * namespace, the same rule the walk enforces.\n */\nfunction preflight(tree: FleetTree, seed: FleetSeed): SeedCommit | null {\n const treeSubs = new Set(tree.subsystems.map((s) => s.name));\n const treeIds = new Set(tree.services.map((s) => s.id as string));\n\n // The collision scan runs BEFORE the missing-service scan, and the order is\n // load-bearing. A name the file declares as a subsystem or an external while\n // the tree holds it as a service DIRECTORY is both facts at once: it is a\n // namespace collision, and it is also an existing service the file does not\n // name. With the missing scan first, the caller was told to paste that name\n // into `services:` — advice that walks straight into `seed-duplicate-service`\n // on the next run, because the file already declares it as something else.\n const collisions: string[] = [];\n for (const sub of seed.subsystems) {\n if (treeIds.has(sub as string)) collisions.push(`subsystem '${sub}' is an existing service directory`);\n }\n for (const s of seed.services) {\n if (treeSubs.has(s.id as string)) collisions.push(`service '${s.id}' is an existing subsystem directory`);\n }\n for (const x of seed.externals) {\n if (treeIds.has(x)) collisions.push(`external '${x}' is an existing service directory`);\n if (treeSubs.has(x)) collisions.push(`external '${x}' is an existing subsystem directory`);\n }\n if (collisions.length > 0) {\n return {\n ok: false,\n code: \"seed-file-invalid\",\n message:\n `fleet.yaml collides with the services/ tree — service ids, subsystem names and externals ` +\n `share one flat namespace: ${collisions.join(\"; \")}. Rename the fleet.yaml side (or the ` +\n `directory, with \\`loam subsystem rename <from> <to>\\`), then re-run. Nothing was written.`,\n missingServices: [],\n };\n }\n\n const ids = new Set(seed.services.map((s) => s.id as string));\n const missing = tree.services.map((s) => s.id as string).filter((id) => !ids.has(id)).sort();\n if (missing.length === 0) return null;\n // The ids are partitioned because one half cannot take the other's advice.\n // `tree.services` carries RAW ids — a directory whose name fails the service\n // grammar is still a service to the walk, by design — and \"add it to\n // services: exactly as spelled\" is impossible for one of those: the fleet\n // file would refuse the entry. Without this split, seed was simply unusable\n // on such a repo and never said so.\n const legal = missing.filter((id) => parseServiceId(id, \"services entry\").ok);\n const illegal = missing.filter((id) => !parseServiceId(id, \"services entry\").ok);\n const fixes: string[] = [];\n if (legal.length > 0) fixes.push(`add ${legal.join(\", \")} to services:, exactly as spelled`);\n if (illegal.length > 0) {\n fixes.push(\n `rename the ${illegal.length === 1 ? \"directory\" : \"directories\"} ${illegal.join(\", \")} ` +\n `first — ${illegal.length === 1 ? \"that name is not a legal service id\" : \"those names are not legal service ids\"}, ` +\n `so no services: entry can name ${illegal.length === 1 ? \"it\" : \"them\"}`,\n );\n }\n return {\n ok: false,\n code: \"seed-file-invalid\",\n message:\n `fleet.yaml must name every existing service — seed regenerates the landscape wholesale, ` +\n `and these services/ directories are not in it: ${missing.join(\", \")}. ` +\n `To fix: ${fixes.join(\"; and \")}. Then re-run. Nothing was written.`,\n missingServices: missing,\n };\n}\n"]}
@@ -1,10 +1,9 @@
1
1
  import { resolve } from "node:path";
2
- import { loadSource } from "../../core/c4/likec4.js";
2
+ import { errorText, loadSource } from "../../core/c4/likec4.js";
3
3
  import { readFleetFile } from "../../core/c4/seed/fleet-file.js";
4
4
  import { sealLandscape } from "../../core/c4/seed/stamp.js";
5
5
  import { renderLandscape } from "../../core/c4/seed/template.js";
6
- import { loadConfig } from "../../core/envelope/config.js";
7
- import { emitJson, emitJsonError, fail, repoPath, reportNoConfig } from "../../core/envelope/json.js";
6
+ import { configOrRefuse, emitJson, emitJsonError, fail, repoPath } from "../../core/envelope/json.js";
8
7
  import { NotUtf8Error, readUtf8 } from "../../core/staging/writes.js";
9
8
  import { sayRecovered } from "../policy/format.js";
10
9
  import { docsRepoReady, reportDocsRepoError } from "../policy/gate.js";
@@ -19,12 +18,10 @@ export function registerSeed(program) {
19
18
  .action(async (opts) => {
20
19
  const json = opts.json === true;
21
20
  const fromArg = opts.from ?? "fleet.yaml";
22
- const loaded = await loadConfig();
23
- if (loaded.kind !== "loaded") {
24
- reportNoConfig(json, loaded);
21
+ const config = await configOrRefuse(json);
22
+ if (config === null)
25
23
  return;
26
- }
27
- const { docsDir } = loaded.config;
24
+ const { docsDir } = config;
28
25
  // `services`, not `docs`: every id in fleet.yaml becomes a
29
26
  // services/<id>/ directory, and the under-lock preflight enumerates the
30
27
  // tree — a run without the directory that IS the fleet is guessing.
@@ -70,7 +67,7 @@ export function registerSeed(program) {
70
67
  const firstError = doc.errors[0];
71
68
  if (firstError !== undefined) {
72
69
  return fail(json, "internal", `loam's own templater produced a landscape that does not parse — a bug in loam, not ` +
73
- `in ${fromArg}; nothing was written. First error: ${firstError.message}`);
70
+ `in ${fromArg}; nothing was written. First error: ${errorText(firstError)}`);
74
71
  }
75
72
  const committed = await commitSeed({
76
73
  docsDir,
@@ -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 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"]}
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 { errorText, 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 { configOrRefuse, emitJson, emitJsonError, fail, repoPath } 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 config = await configOrRefuse(json);\n if (config === null) return;\n const { docsDir } = 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: ${errorText(firstError)}`,\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"]}
@@ -1,6 +1,5 @@
1
- import { loadConfig } from "../../core/envelope/config.js";
2
1
  import { FleetContext } from "../../core/fleet-context.js";
3
- import { fail, reportNoConfig } from "../../core/envelope/json.js";
2
+ import { configOrRefuse, fail } from "../../core/envelope/json.js";
4
3
  import { DocsRepoUnavailableError } from "../../core/repo/state.js";
5
4
  import { listServices, resolveFeature } from "../../core/repo/repo.js";
6
5
  import { docsRepoReady, reportDocsRepoError, reportRepositoryUnavailable } from "../policy/gate.js";
@@ -20,12 +19,9 @@ export function registerShow(program) {
20
19
  return;
21
20
  }
22
21
  const forced = opts.type;
23
- const loaded = await loadConfig();
24
- if (loaded.kind !== "loaded") {
25
- reportNoConfig(json, loaded);
22
+ const config = await configOrRefuse(json);
23
+ if (config === null)
26
24
  return;
27
- }
28
- const config = loaded.config;
29
25
  const { docsDir } = config;
30
26
  // "No service or feature 'x' in <dir>" is a lie when <dir> is not a docs
31
27
  // repo at all — the same refusal validate and list owe (docsRepoReady).
@@ -1 +1 @@
1
- {"version":3,"file":"show.js","sourceRoot":"","sources":["../../../src/commands/show/show.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Command } from \"commander\";\nimport { loadConfig } from \"../../core/envelope/config.js\";\nimport { FleetContext } from \"../../core/fleet-context.js\";\nimport { fail, reportNoConfig } from \"../../core/envelope/json.js\";\nimport { DocsRepoUnavailableError } from \"../../core/repo/state.js\";\nimport { listServices, resolveFeature } from \"../../core/repo/repo.js\";\nimport { docsRepoReady, reportDocsRepoError, reportRepositoryUnavailable } from \"../policy/gate.js\";\nimport { showFeature } from \"./feature.js\";\nimport { showService } from \"./service.js\";\n\ntype TargetType = \"service\" | \"feature\";\n\ninterface ShowOptions {\n json?: boolean;\n type?: string;\n}\n\nexport function registerShow(program: Command): void {\n program\n .command(\"show\")\n .argument(\"<target>\", \"service id or feature id\")\n .description(\"Show everything loam knows about a service or a feature\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .option(\"--type <kind>\", \"force the reading: service | feature\")\n .action(async (target: string, opts: ShowOptions) => {\n const json = opts.json === true;\n if (opts.type !== undefined && opts.type !== \"service\" && opts.type !== \"feature\") {\n fail(json, \"invalid-option\", `Unknown --type '${opts.type}'. Expected: service | feature.`);\n return;\n }\n const forced = opts.type as TargetType | undefined;\n\n const loaded = await loadConfig();\n if (loaded.kind !== \"loaded\") {\n reportNoConfig(json, loaded);\n return;\n }\n const config = loaded.config;\n const { docsDir } = config;\n // \"No service or feature 'x' in <dir>\" is a lie when <dir> is not a docs\n // repo at all — the same refusal validate and list owe (docsRepoReady).\n // `docs`, not `services`: a feature is readable from a repo whose\n // services/ is missing, and if the target turns out to be a service the\n // enumeration below refuses with `services-missing` anyway.\n if (!docsRepoReady(json, docsDir, \"docs\")) return;\n // One read index for the invocation. Deciding the target is a service\n // enumerates the fleet, and so does resolving the landscape's edges to the\n // services that own them — `loam show <service>` walked services/ twice for\n // one answer. The context memoises only within this invocation, so a later\n // run still sees whatever is on disk then.\n const context = new FleetContext();\n\n try {\n // A feature id is distinctive (FEAT-101); a service name is arbitrary. When\n // both could match, the feature wins and --type forces the other reading.\n const feature =\n forced === \"service\" ? null : await resolveFeature(docsDir, target, \"include\", context);\n if (feature) {\n await showFeature(docsDir, feature, json, context);\n return;\n }\n // The enumeration's own id travels on — `entry.id` rather than the raw\n // argument (repo/service-target.ts's rule): equal as strings, and only\n // one of them carries the fact that a readdir produced it.\n const entry =\n forced === \"feature\" ? undefined : (await listServices(docsDir, context)).find((s) => s.id === target);\n if (entry !== undefined) {\n await showService(docsDir, entry.id, json, context);\n return;\n }\n\n const looked = forced ? forced : \"service or feature\";\n fail(json, \"unknown-target\", `No ${looked} '${target}' in ${docsDir}.`);\n } catch (err) {\n if (err instanceof DocsRepoUnavailableError) {\n reportDocsRepoError(json, err);\n return;\n }\n // An artifact that exists but cannot be read used to escape as a stack\n // trace (`internal` in --json), naming nothing. `show` reads one target,\n // so it has nothing to fall back on — but it can at least say WHICH file.\n reportRepositoryUnavailable(json, err, `'${target}' cannot be shown`, docsDir);\n }\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* Service */\n/* ------------------------------------------------------------------ */\n"]}
1
+ {"version":3,"file":"show.js","sourceRoot":"","sources":["../../../src/commands/show/show.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Command } from \"commander\";\nimport { FleetContext } from \"../../core/fleet-context.js\";\nimport { configOrRefuse, fail } from \"../../core/envelope/json.js\";\nimport { DocsRepoUnavailableError } from \"../../core/repo/state.js\";\nimport { listServices, resolveFeature } from \"../../core/repo/repo.js\";\nimport { docsRepoReady, reportDocsRepoError, reportRepositoryUnavailable } from \"../policy/gate.js\";\nimport { showFeature } from \"./feature.js\";\nimport { showService } from \"./service.js\";\n\ntype TargetType = \"service\" | \"feature\";\n\ninterface ShowOptions {\n json?: boolean;\n type?: string;\n}\n\nexport function registerShow(program: Command): void {\n program\n .command(\"show\")\n .argument(\"<target>\", \"service id or feature id\")\n .description(\"Show everything loam knows about a service or a feature\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .option(\"--type <kind>\", \"force the reading: service | feature\")\n .action(async (target: string, opts: ShowOptions) => {\n const json = opts.json === true;\n if (opts.type !== undefined && opts.type !== \"service\" && opts.type !== \"feature\") {\n fail(json, \"invalid-option\", `Unknown --type '${opts.type}'. Expected: service | feature.`);\n return;\n }\n const forced = opts.type as TargetType | undefined;\n\n const config = await configOrRefuse(json);\n if (config === null) return;\n const { docsDir } = config;\n // \"No service or feature 'x' in <dir>\" is a lie when <dir> is not a docs\n // repo at all — the same refusal validate and list owe (docsRepoReady).\n // `docs`, not `services`: a feature is readable from a repo whose\n // services/ is missing, and if the target turns out to be a service the\n // enumeration below refuses with `services-missing` anyway.\n if (!docsRepoReady(json, docsDir, \"docs\")) return;\n // One read index for the invocation. Deciding the target is a service\n // enumerates the fleet, and so does resolving the landscape's edges to the\n // services that own them — `loam show <service>` walked services/ twice for\n // one answer. The context memoises only within this invocation, so a later\n // run still sees whatever is on disk then.\n const context = new FleetContext();\n\n try {\n // A feature id is distinctive (FEAT-101); a service name is arbitrary. When\n // both could match, the feature wins and --type forces the other reading.\n const feature =\n forced === \"service\" ? null : await resolveFeature(docsDir, target, \"include\", context);\n if (feature) {\n await showFeature(docsDir, feature, json, context);\n return;\n }\n // The enumeration's own id travels on — `entry.id` rather than the raw\n // argument (repo/service-target.ts's rule): equal as strings, and only\n // one of them carries the fact that a readdir produced it.\n const entry =\n forced === \"feature\" ? undefined : (await listServices(docsDir, context)).find((s) => s.id === target);\n if (entry !== undefined) {\n await showService(docsDir, entry.id, json, context);\n return;\n }\n\n const looked = forced ? forced : \"service or feature\";\n fail(json, \"unknown-target\", `No ${looked} '${target}' in ${docsDir}.`);\n } catch (err) {\n if (err instanceof DocsRepoUnavailableError) {\n reportDocsRepoError(json, err);\n return;\n }\n // An artifact that exists but cannot be read used to escape as a stack\n // trace (`internal` in --json), naming nothing. `show` reads one target,\n // so it has nothing to fall back on — but it can at least say WHICH file.\n reportRepositoryUnavailable(json, err, `'${target}' cannot be shown`, docsDir);\n }\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* Service */\n/* ------------------------------------------------------------------ */\n"]}
@@ -5,6 +5,7 @@
5
5
  * one of them is a contract. This side may be reworded freely; the payload
6
6
  * beside it may not.
7
7
  */
8
+ import { answeredSplit } from "../../core/verify/record.js";
8
9
  import { SEVERITY_MARK } from "../../core/vocabulary/report.js";
9
10
  function pad(values) {
10
11
  return Math.max(0, ...values.map((v) => v.length));
@@ -33,6 +34,10 @@ function artifactLabel(a) {
33
34
  * because they answer different questions and only together are they honest:
34
35
  * `recorded 4/4 confirmed` used to be the whole line for a feature `verify`
35
36
  * calls `attested` — complete, and resting on somebody's word about a test.
37
+ * Who answered the confirmed ones rides right after the count for the same
38
+ * reason one step further: `verified` turns on the scenario claims alone, and
39
+ * `verified 19/19 confirmed` read as nineteen runs over a record where six
40
+ * answers were an agent's word about a permission or an edge.
36
41
  */
37
42
  function verificationLine(v) {
38
43
  if (v.state === "absent")
@@ -40,6 +45,7 @@ function verificationLine(v) {
40
45
  if (v.state === "unreadable")
41
46
  return "unreadable";
42
47
  return (`${v.state} · ${v.verdict} ${v.confirmed}/${v.claims} confirmed` +
48
+ (v.confirmed > 0 ? ` (${answeredSplit(v.answered)})` : "") +
43
49
  (v.unanswered > 0 ? ` · ${v.unanswered} unanswered` : "") +
44
50
  (v.attested > 0 ? ` · ${v.attested} scenario(s) on an agent's word, not a test run` : "") +
45
51
  ` (recorded ${v.recorded ?? "?"})`);
@@ -130,11 +136,14 @@ export function printFleet(r) {
130
136
  printInterrupted(r.interrupted);
131
137
  const s = r.services;
132
138
  console.log(`loam status — ${s.total} service(s) · ${r.features.length} feature(s) in flight`);
133
- console.log(
134
- // The sampled count inside the vouched one, the scorecard's own layout: a
135
- // reader who stops at the first number must not have been told the
139
+ // The sampled and stale counts inside the vouched one, the scorecard's own
140
+ // layout: a reader who stops at the first number must not have been told the
136
141
  // stronger of the two.
137
- ` services ${s.undocumented} undocumented · ${s.draft} draft · ${s.vouched} vouched${s.sampledVouched > 0 ? ` (${s.sampledVouched} sampled)` : ""}`);
142
+ const notes = [
143
+ ...(s.sampledVouched > 0 ? [`${s.sampledVouched} sampled`] : []),
144
+ ...(s.contentStale > 0 ? [`${s.contentStale} stale`] : []),
145
+ ];
146
+ console.log(` services ${s.undocumented} undocumented · ${s.draft} draft · ${s.vouched} vouched${notes.length > 0 ? ` (${notes.join(", ")})` : ""}`);
138
147
  if (r.service !== null)
139
148
  console.log(` narrowed to ${r.service}`);
140
149
  if (r.order.length > 1)
@@ -1 +1 @@
1
- {"version":3,"file":"print.js","sourceRoot":"","sources":["../../../src/commands/status/print.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The human view of a status: one feature, or the whole fleet.\n *\n * Separate from `./json.ts` because the two answer different readers and only\n * one of them is a contract. This side may be reworded freely; the payload\n * beside it may not.\n */\nimport { SEVERITY_MARK } from \"../../core/vocabulary/report.js\";\nimport type {\n ArtifactState,\n FeatureStatusReport,\n FleetStatusReport,\n InterruptedCommit,\n NextStep,\n VerificationState,\n} from \"../../core/status/report.js\";\n\nfunction pad(values: string[]): number {\n return Math.max(0, ...values.map((v) => v.length));\n}\n\n/**\n * The half-merged-repo banner, printed FIRST and on stderr.\n *\n * stderr, alone among this command's output, because it is not part of the\n * answer: it is the reason the answer may be wrong. A reader piping `status`\n * into a pager still sees it, and the human view stays the shape every other\n * run has. `--json` carries the same fact in `interrupted`.\n */\nfunction printInterrupted(i: InterruptedCommit): void {\n console.error(\n i.unreadable\n ? \"⚠ this docs repo holds a .loam-commit that cannot be read — a commit was interrupted and nothing here can be trusted until a human compares the living docs against version control.\"\n : `⚠ a \\`${i.recover}\\` was killed mid-commit (${i.host}, pid ${i.pid}, ${i.at}) — ${i.files.length} file(s) may be half-written: ${i.files.join(\", \")}. Everything below is derived from them.`,\n );\n console.error(\"\");\n}\n\n/** `spec (payment-service)` — the artifact id plus the service, when it has one. */\nfunction artifactLabel(a: ArtifactState): string {\n return a.service === null ? a.id : `${a.id} (${a.service})`;\n}\n\n/**\n * The counts come from the record's `claims:` array (verify's `tallyRecord`),\n * never from its `summary:` block. The verdict is printed beside the state\n * because they answer different questions and only together are they honest:\n * `recorded 4/4 confirmed` used to be the whole line for a feature `verify`\n * calls `attested` — complete, and resting on somebody's word about a test.\n */\nfunction verificationLine(v: VerificationState): string {\n if (v.state === \"absent\") return \"none\";\n if (v.state === \"unreadable\") return \"unreadable\";\n return (\n `${v.state} · ${v.verdict} ${v.confirmed}/${v.claims} confirmed` +\n (v.unanswered > 0 ? ` · ${v.unanswered} unanswered` : \"\") +\n (v.attested > 0 ? ` · ${v.attested} scenario(s) on an agent's word, not a test run` : \"\") +\n ` (recorded ${v.recorded ?? \"?\"})`\n );\n}\n\n/**\n * The steps, numbered, with the command on its own indented line — doctor's\n * layout, for doctor's reason: what to do and what to type are two different\n * sentences, and someone scanning a half-built feature reads down the `run:`\n * column.\n */\nfunction printNext(steps: NextStep[]): void {\n console.log(\"\\n next\");\n for (const [i, step] of steps.entries()) {\n console.log(` ${i + 1}. ${step.code}: ${step.statement}`);\n console.log(` run: ${step.command}`);\n }\n}\n\n/**\n * The business flows the services in view already appear in.\n *\n * Silent when the fleet declares none, unlike the delta brief's version of the\n * same section: this report is a table of what is OWED, and a \"(none)\" row for\n * a fleet that has never drawn a use case would be one more line between the\n * reader and the next step. The unreadable arm is not silent, because that one\n * is a hole rather than an absence.\n */\nfunction printUseCases(u: FeatureStatusReport[\"useCases\"]): void {\n if (u.unreadable) {\n console.log(\"\\n use cases architecture/ does not parse, so the flows through these services could not be read\");\n return;\n }\n if (u.flows.length === 0) return;\n console.log(\"\\n use cases\");\n for (const flow of u.flows) {\n console.log(` ${flow.title ?? flow.id} [${flow.id}] ${flow.file}`);\n for (const step of flow.steps) {\n const label = step.title === undefined ? \"\" : ` '${step.title}'`;\n console.log(` step ${step.ordinal}${label}: ${step.source} -> ${step.target}`);\n }\n }\n}\n\nexport function printFeature(r: FeatureStatusReport, ambiguous: string[]): void {\n if (r.interrupted !== null) printInterrupted(r.interrupted);\n const f = r.feature;\n console.log(`loam status ${f.id} — ${f.stage}${f.archived ? \" (archived)\" : \"\"}`);\n console.log(` feature ${f.dirName} ${f.path}`);\n console.log(` services ${f.services.length > 0 ? f.services.join(\", \") : \"(none)\"}`);\n if (r.service !== null) console.log(` narrowed to ${r.service}`);\n if (f.blockedBy.length > 0) console.log(` waiting on ${f.blockedBy.join(\", \")}`);\n console.log(\n ` coherence ${\n r.checks.ran\n ? `${r.checks.errors} error(s) · ${r.checks.warnings} warning(s) · ${r.checks.gating} gate archive`\n : \"not run (archived)\"\n }`,\n );\n console.log(` verification ${verificationLine(r.verification)}`);\n if (ambiguous.length > 0) {\n console.log(` note '${f.id}' also names ${ambiguous.filter((d) => d !== f.dirName).join(\", \")}`);\n }\n\n console.log(\"\\n artifacts\");\n const labels = r.artifacts.map(artifactLabel);\n const labelWidth = pad(labels);\n const statusWidth = pad(r.artifacts.map((a) => a.status));\n for (const [i, a] of r.artifacts.entries()) {\n // `done` on a file that is not there is the one row a reader misreads: it\n // means the feature owes no such artifact, not that one was written. The\n // JSON contract says it with `exists`; the human view has to say it in\n // words, or the table quietly claims files that do not exist.\n const note =\n a.blockedBy.length > 0\n ? ` (needs ${a.blockedBy.join(\", \")})`\n : !a.exists && a.status === \"done\"\n ? \" (not written — none owed)\"\n : \"\";\n console.log(\n ` ${a.status.padEnd(statusWidth)} ${labels[i]!.padEnd(labelWidth)} ${a.path}${note}`.trimEnd(),\n );\n }\n\n if (r.checks.issues.length > 0) {\n console.log(\"\\n findings\");\n for (const issue of r.checks.issues) {\n console.log(` ${SEVERITY_MARK[issue.severity]} ${issue.code}: ${issue.message}`);\n }\n }\n\n printUseCases(r.useCases);\n printNext(r.next);\n}\n\nexport function printFleet(r: FleetStatusReport): void {\n if (r.interrupted !== null) printInterrupted(r.interrupted);\n const s = r.services;\n console.log(`loam status — ${s.total} service(s) · ${r.features.length} feature(s) in flight`);\n console.log(\n // The sampled count inside the vouched one, the scorecard's own layout: a\n // reader who stops at the first number must not have been told the\n // stronger of the two.\n ` services ${s.undocumented} undocumented · ${s.draft} draft · ${s.vouched} vouched${s.sampledVouched > 0 ? ` (${s.sampledVouched} sampled)` : \"\"}`,\n );\n if (r.service !== null) console.log(` narrowed to ${r.service}`);\n if (r.order.length > 1) console.log(` order ${r.order.join(\" → \")}`);\n\n if (r.features.length > 0) {\n console.log(\"\\n features\");\n const ids = pad(r.features.map((f) => f.id));\n const stages = pad(r.features.map((f) => f.stage));\n for (const f of r.features) {\n const detail =\n f.blockedBy.length > 0\n ? `waiting on ${f.blockedBy.join(\", \")}`\n : f.missing.length > 0\n ? `missing ${f.missing.join(\", \")}`\n : f.services.join(\", \");\n console.log(` ${f.stage.padEnd(stages)} ${f.id.padEnd(ids)} ${detail}`.trimEnd());\n }\n }\n\n printNext(r.next);\n}\n"]}
1
+ {"version":3,"file":"print.js","sourceRoot":"","sources":["../../../src/commands/status/print.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The human view of a status: one feature, or the whole fleet.\n *\n * Separate from `./json.ts` because the two answer different readers and only\n * one of them is a contract. This side may be reworded freely; the payload\n * beside it may not.\n */\nimport { answeredSplit } from \"../../core/verify/record.js\";\nimport { SEVERITY_MARK } from \"../../core/vocabulary/report.js\";\nimport type {\n ArtifactState,\n FeatureStatusReport,\n FleetStatusReport,\n InterruptedCommit,\n NextStep,\n VerificationState,\n} from \"../../core/status/report.js\";\n\nfunction pad(values: string[]): number {\n return Math.max(0, ...values.map((v) => v.length));\n}\n\n/**\n * The half-merged-repo banner, printed FIRST and on stderr.\n *\n * stderr, alone among this command's output, because it is not part of the\n * answer: it is the reason the answer may be wrong. A reader piping `status`\n * into a pager still sees it, and the human view stays the shape every other\n * run has. `--json` carries the same fact in `interrupted`.\n */\nfunction printInterrupted(i: InterruptedCommit): void {\n console.error(\n i.unreadable\n ? \"⚠ this docs repo holds a .loam-commit that cannot be read — a commit was interrupted and nothing here can be trusted until a human compares the living docs against version control.\"\n : `⚠ a \\`${i.recover}\\` was killed mid-commit (${i.host}, pid ${i.pid}, ${i.at}) — ${i.files.length} file(s) may be half-written: ${i.files.join(\", \")}. Everything below is derived from them.`,\n );\n console.error(\"\");\n}\n\n/** `spec (payment-service)` — the artifact id plus the service, when it has one. */\nfunction artifactLabel(a: ArtifactState): string {\n return a.service === null ? a.id : `${a.id} (${a.service})`;\n}\n\n/**\n * The counts come from the record's `claims:` array (verify's `tallyRecord`),\n * never from its `summary:` block. The verdict is printed beside the state\n * because they answer different questions and only together are they honest:\n * `recorded 4/4 confirmed` used to be the whole line for a feature `verify`\n * calls `attested` — complete, and resting on somebody's word about a test.\n * Who answered the confirmed ones rides right after the count for the same\n * reason one step further: `verified` turns on the scenario claims alone, and\n * `verified 19/19 confirmed` read as nineteen runs over a record where six\n * answers were an agent's word about a permission or an edge.\n */\nfunction verificationLine(v: VerificationState): string {\n if (v.state === \"absent\") return \"none\";\n if (v.state === \"unreadable\") return \"unreadable\";\n return (\n `${v.state} · ${v.verdict} ${v.confirmed}/${v.claims} confirmed` +\n (v.confirmed > 0 ? ` (${answeredSplit(v.answered)})` : \"\") +\n (v.unanswered > 0 ? ` · ${v.unanswered} unanswered` : \"\") +\n (v.attested > 0 ? ` · ${v.attested} scenario(s) on an agent's word, not a test run` : \"\") +\n ` (recorded ${v.recorded ?? \"?\"})`\n );\n}\n\n/**\n * The steps, numbered, with the command on its own indented line — doctor's\n * layout, for doctor's reason: what to do and what to type are two different\n * sentences, and someone scanning a half-built feature reads down the `run:`\n * column.\n */\nfunction printNext(steps: NextStep[]): void {\n console.log(\"\\n next\");\n for (const [i, step] of steps.entries()) {\n console.log(` ${i + 1}. ${step.code}: ${step.statement}`);\n console.log(` run: ${step.command}`);\n }\n}\n\n/**\n * The business flows the services in view already appear in.\n *\n * Silent when the fleet declares none, unlike the delta brief's version of the\n * same section: this report is a table of what is OWED, and a \"(none)\" row for\n * a fleet that has never drawn a use case would be one more line between the\n * reader and the next step. The unreadable arm is not silent, because that one\n * is a hole rather than an absence.\n */\nfunction printUseCases(u: FeatureStatusReport[\"useCases\"]): void {\n if (u.unreadable) {\n console.log(\"\\n use cases architecture/ does not parse, so the flows through these services could not be read\");\n return;\n }\n if (u.flows.length === 0) return;\n console.log(\"\\n use cases\");\n for (const flow of u.flows) {\n console.log(` ${flow.title ?? flow.id} [${flow.id}] ${flow.file}`);\n for (const step of flow.steps) {\n const label = step.title === undefined ? \"\" : ` '${step.title}'`;\n console.log(` step ${step.ordinal}${label}: ${step.source} -> ${step.target}`);\n }\n }\n}\n\nexport function printFeature(r: FeatureStatusReport, ambiguous: string[]): void {\n if (r.interrupted !== null) printInterrupted(r.interrupted);\n const f = r.feature;\n console.log(`loam status ${f.id} — ${f.stage}${f.archived ? \" (archived)\" : \"\"}`);\n console.log(` feature ${f.dirName} ${f.path}`);\n console.log(` services ${f.services.length > 0 ? f.services.join(\", \") : \"(none)\"}`);\n if (r.service !== null) console.log(` narrowed to ${r.service}`);\n if (f.blockedBy.length > 0) console.log(` waiting on ${f.blockedBy.join(\", \")}`);\n console.log(\n ` coherence ${\n r.checks.ran\n ? `${r.checks.errors} error(s) · ${r.checks.warnings} warning(s) · ${r.checks.gating} gate archive`\n : \"not run (archived)\"\n }`,\n );\n console.log(` verification ${verificationLine(r.verification)}`);\n if (ambiguous.length > 0) {\n console.log(` note '${f.id}' also names ${ambiguous.filter((d) => d !== f.dirName).join(\", \")}`);\n }\n\n console.log(\"\\n artifacts\");\n const labels = r.artifacts.map(artifactLabel);\n const labelWidth = pad(labels);\n const statusWidth = pad(r.artifacts.map((a) => a.status));\n for (const [i, a] of r.artifacts.entries()) {\n // `done` on a file that is not there is the one row a reader misreads: it\n // means the feature owes no such artifact, not that one was written. The\n // JSON contract says it with `exists`; the human view has to say it in\n // words, or the table quietly claims files that do not exist.\n const note =\n a.blockedBy.length > 0\n ? ` (needs ${a.blockedBy.join(\", \")})`\n : !a.exists && a.status === \"done\"\n ? \" (not written — none owed)\"\n : \"\";\n console.log(\n ` ${a.status.padEnd(statusWidth)} ${labels[i]!.padEnd(labelWidth)} ${a.path}${note}`.trimEnd(),\n );\n }\n\n if (r.checks.issues.length > 0) {\n console.log(\"\\n findings\");\n for (const issue of r.checks.issues) {\n console.log(` ${SEVERITY_MARK[issue.severity]} ${issue.code}: ${issue.message}`);\n }\n }\n\n printUseCases(r.useCases);\n printNext(r.next);\n}\n\nexport function printFleet(r: FleetStatusReport): void {\n if (r.interrupted !== null) printInterrupted(r.interrupted);\n const s = r.services;\n console.log(`loam status — ${s.total} service(s) · ${r.features.length} feature(s) in flight`);\n // The sampled and stale counts inside the vouched one, the scorecard's own\n // layout: a reader who stops at the first number must not have been told the\n // stronger of the two.\n const notes = [\n ...(s.sampledVouched > 0 ? [`${s.sampledVouched} sampled`] : []),\n ...(s.contentStale > 0 ? [`${s.contentStale} stale`] : []),\n ];\n console.log(\n ` services ${s.undocumented} undocumented · ${s.draft} draft · ${s.vouched} vouched${notes.length > 0 ? ` (${notes.join(\", \")})` : \"\"}`,\n );\n if (r.service !== null) console.log(` narrowed to ${r.service}`);\n if (r.order.length > 1) console.log(` order ${r.order.join(\" → \")}`);\n\n if (r.features.length > 0) {\n console.log(\"\\n features\");\n const ids = pad(r.features.map((f) => f.id));\n const stages = pad(r.features.map((f) => f.stage));\n for (const f of r.features) {\n const detail =\n f.blockedBy.length > 0\n ? `waiting on ${f.blockedBy.join(\", \")}`\n : f.missing.length > 0\n ? `missing ${f.missing.join(\", \")}`\n : f.services.join(\", \");\n console.log(` ${f.stage.padEnd(stages)} ${f.id.padEnd(ids)} ${detail}`.trimEnd());\n }\n }\n\n printNext(r.next);\n}\n"]}
@@ -1,6 +1,5 @@
1
- import { loadConfig } from "../../core/envelope/config.js";
2
1
  import { FleetContext } from "../../core/fleet-context.js";
3
- import { emitJson, fail, reportNoConfig } from "../../core/envelope/json.js";
2
+ import { configOrRefuse, emitJson, fail } from "../../core/envelope/json.js";
4
3
  import { DocsRepoUnavailableError } from "../../core/repo/state.js";
5
4
  import { featureCandidates, missingFeatureMessage, resolveFeature } from "../../core/repo/repo.js";
6
5
  import { featureStatus } from "../../core/status/feature/feature.js";
@@ -17,12 +16,9 @@ export function registerStatus(program) {
17
16
  .option("--service <id>", "narrow the per-service view to one service")
18
17
  .action(async (featureArg, opts) => {
19
18
  const json = opts.json === true;
20
- const loaded = await loadConfig();
21
- if (loaded.kind !== "loaded") {
22
- reportNoConfig(json, loaded);
19
+ const config = await configOrRefuse(json);
20
+ if (config === null)
23
21
  return;
24
- }
25
- const config = loaded.config;
26
22
  const { docsDir } = config;
27
23
  // A docsDir that is not a docs repo is refused, never rendered as a repo
28
24
  // with nothing in it — the doctrine docs-repo-gate.ts's docsRepoReady exists
@@ -1 +1 @@
1
- {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/commands/status/status.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Command } from \"commander\";\nimport { loadConfig } from \"../../core/envelope/config.js\";\nimport { FleetContext } from \"../../core/fleet-context.js\";\nimport { emitJson, fail, reportNoConfig } from \"../../core/envelope/json.js\";\nimport { DocsRepoUnavailableError } from \"../../core/repo/state.js\";\nimport { featureCandidates, missingFeatureMessage, resolveFeature } from \"../../core/repo/repo.js\";\nimport { featureStatus } from \"../../core/status/feature/feature.js\";\nimport { fleetStatus } from \"../../core/status/fleet/fleet.js\";\nimport { docsRepoReady, reportDocsRepoError, reportRepositoryUnavailable } from \"../policy/gate.js\";\nimport { featureJson, fleetJson } from \"./json.js\";\nimport { printFeature, printFleet } from \"./print.js\";\n\n/**\n * `loam status` — the read-only projection. Nothing here computes anything: the\n * derivation lives in core/status/ (and, underneath it, in the same coherence\n * and verification calls `validate`, `verify` and `archive` make). This file\n * resolves the target, refuses the arguments it cannot honour, and renders.\n *\n * It reports and does not gate, which is why it never sets a non-zero exit code\n * on a successful run — see `registerStatus`.\n */\n\ninterface StatusOptions {\n json?: boolean;\n service?: string;\n}\n\nexport function registerStatus(program: Command): void {\n program\n .command(\"status\")\n .argument(\"[feature]\", \"feature id or directory name; omit for the whole repository\")\n .description(\"Report where the work stands and what to do next, derived from the files\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .option(\"--service <id>\", \"narrow the per-service view to one service\")\n .action(async (featureArg: string | undefined, opts: StatusOptions) => {\n const json = opts.json === true;\n const loaded = await loadConfig();\n if (loaded.kind !== \"loaded\") {\n reportNoConfig(json, loaded);\n return;\n }\n const config = loaded.config;\n const { docsDir } = config;\n // A docsDir that is not a docs repo is refused, never rendered as a repo\n // with nothing in it — the doctrine docs-repo-gate.ts's docsRepoReady exists\n // for. `status` counts services, so it needs services/ in both modes.\n if (!docsRepoReady(json, docsDir, \"services\")) return;\n const context = new FleetContext();\n // What the refusal below says it was answering when it gave up.\n let scope = \"this repository\";\n\n try {\n if (featureArg === undefined) {\n // `bound` is what THIS repo says it is, which is a different fact from\n // `--service` (which narrows the view). Passing it is what lets the\n // fleet form notice that the repository it is standing in has never\n // been adopted — the one thing a fleet count cannot see.\n const report = await fleetStatus(docsDir, {\n service: opts.service,\n bound: config.service,\n context,\n });\n // A `--service` that names nothing is refused rather than answered\n // with an empty fleet: \"no services and no features\" and \"you\n // misspelled it\" are opposite facts, and only one of them is worth\n // acting on.\n if (opts.service !== undefined && report.services.total === 0) {\n fail(json, \"unknown-service\", `No service '${opts.service}' under ${docsDir}/services/.`);\n return;\n }\n if (json) emitJson({ command: \"status\", docsDir, scope: \"fleet\", ...fleetJson(report) });\n else printFleet(report);\n return;\n }\n\n // Archived features are IN scope: an agent asking where FEAT-101 stands\n // after it shipped deserves \"it shipped\", not \"no such feature\" — the\n // same reading `show` takes. An argument naming two directories is\n // still answered (status writes nothing), with the tie broken the way\n // resolveFeature always breaks it.\n const feature = await resolveFeature(docsDir, featureArg, \"include\", context);\n if (!feature) {\n fail(json, \"unknown-target\", await missingFeatureMessage(docsDir, featureArg, context));\n return;\n }\n scope = feature.id;\n // `config.service` is not a lens like `--service`: it says which\n // repository this IS, and three of the steps (`loam gherkin`, `verify\n // --record --service`) refuse anywhere else. Without it status handed a\n // docs-repo reader commands only a service repo can run, and never told\n // a service repo that the work in front of it was its own.\n const report = await featureStatus(docsDir, feature, {\n service: opts.service,\n boundService: config.service,\n context,\n });\n if (opts.service !== undefined && !report.feature.services.includes(opts.service)) {\n fail(\n json,\n \"unknown-service\",\n `${feature.id} carries no delta for '${opts.service}'. It touches: ${\n report.feature.services.length > 0 ? report.feature.services.join(\", \") : \"(no services)\"\n }.`,\n );\n return;\n }\n const ambiguous = await featureCandidates(docsDir, featureArg, \"include\", context);\n if (json) {\n emitJson({ command: \"status\", docsDir, scope: \"feature\", ...featureJson(report) });\n } else {\n printFeature(report, ambiguous.length > 1 ? ambiguous.map((c) => c.dirName) : []);\n }\n } catch (err) {\n if (err instanceof DocsRepoUnavailableError) {\n reportDocsRepoError(json, err);\n return;\n }\n // One unreadable artifact must not escape as a stack trace and an\n // `internal` envelope that names nothing: the projection is\n // all-or-nothing (a status missing one artifact reads as a feature that\n // does not owe it), so the honest answer is a refusal naming the path.\n // The errno-vs-path reading this branch depends on — the reason one\n // directory where a file belongs used to kill BOTH forms of status —\n // now lives once, with its reasoning, on the shared helper.\n reportRepositoryUnavailable(\n json,\n err,\n `the status of ${scope} would be missing an artifact`,\n docsDir,\n );\n }\n // No exit code is set on any path above that succeeded, deliberately.\n // `doctor` exits 1 because a broken installation makes every later\n // command lie, and `validate` exits 1 because it IS the fleet gate. This\n // command is a question, and its answer is equally true whether the\n // feature is finished or not — an agent runs it precisely BECAUSE work is\n // outstanding, and exiting 1 there would make every `set -e` script and\n // every CI step read \"there is work to do\" as a failure.\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* JSON */\n/* ------------------------------------------------------------------ */\n"]}
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/commands/status/status.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Command } from \"commander\";\nimport { FleetContext } from \"../../core/fleet-context.js\";\nimport { configOrRefuse, emitJson, fail } from \"../../core/envelope/json.js\";\nimport { DocsRepoUnavailableError } from \"../../core/repo/state.js\";\nimport { featureCandidates, missingFeatureMessage, resolveFeature } from \"../../core/repo/repo.js\";\nimport { featureStatus } from \"../../core/status/feature/feature.js\";\nimport { fleetStatus } from \"../../core/status/fleet/fleet.js\";\nimport { docsRepoReady, reportDocsRepoError, reportRepositoryUnavailable } from \"../policy/gate.js\";\nimport { featureJson, fleetJson } from \"./json.js\";\nimport { printFeature, printFleet } from \"./print.js\";\n\n/**\n * `loam status` — the read-only projection. Nothing here computes anything: the\n * derivation lives in core/status/ (and, underneath it, in the same coherence\n * and verification calls `validate`, `verify` and `archive` make). This file\n * resolves the target, refuses the arguments it cannot honour, and renders.\n *\n * It reports and does not gate, which is why it never sets a non-zero exit code\n * on a successful run — see `registerStatus`.\n */\n\ninterface StatusOptions {\n json?: boolean;\n service?: string;\n}\n\nexport function registerStatus(program: Command): void {\n program\n .command(\"status\")\n .argument(\"[feature]\", \"feature id or directory name; omit for the whole repository\")\n .description(\"Report where the work stands and what to do next, derived from the files\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .option(\"--service <id>\", \"narrow the per-service view to one service\")\n .action(async (featureArg: string | undefined, opts: StatusOptions) => {\n const json = opts.json === true;\n const config = await configOrRefuse(json);\n if (config === null) return;\n const { docsDir } = config;\n // A docsDir that is not a docs repo is refused, never rendered as a repo\n // with nothing in it — the doctrine docs-repo-gate.ts's docsRepoReady exists\n // for. `status` counts services, so it needs services/ in both modes.\n if (!docsRepoReady(json, docsDir, \"services\")) return;\n const context = new FleetContext();\n // What the refusal below says it was answering when it gave up.\n let scope = \"this repository\";\n\n try {\n if (featureArg === undefined) {\n // `bound` is what THIS repo says it is, which is a different fact from\n // `--service` (which narrows the view). Passing it is what lets the\n // fleet form notice that the repository it is standing in has never\n // been adopted — the one thing a fleet count cannot see.\n const report = await fleetStatus(docsDir, {\n service: opts.service,\n bound: config.service,\n context,\n });\n // A `--service` that names nothing is refused rather than answered\n // with an empty fleet: \"no services and no features\" and \"you\n // misspelled it\" are opposite facts, and only one of them is worth\n // acting on.\n if (opts.service !== undefined && report.services.total === 0) {\n fail(json, \"unknown-service\", `No service '${opts.service}' under ${docsDir}/services/.`);\n return;\n }\n if (json) emitJson({ command: \"status\", docsDir, scope: \"fleet\", ...fleetJson(report) });\n else printFleet(report);\n return;\n }\n\n // Archived features are IN scope: an agent asking where FEAT-101 stands\n // after it shipped deserves \"it shipped\", not \"no such feature\" — the\n // same reading `show` takes. An argument naming two directories is\n // still answered (status writes nothing), with the tie broken the way\n // resolveFeature always breaks it.\n const feature = await resolveFeature(docsDir, featureArg, \"include\", context);\n if (!feature) {\n fail(json, \"unknown-target\", await missingFeatureMessage(docsDir, featureArg, context));\n return;\n }\n scope = feature.id;\n // `config.service` is not a lens like `--service`: it says which\n // repository this IS, and three of the steps (`loam gherkin`, `verify\n // --record --service`) refuse anywhere else. Without it status handed a\n // docs-repo reader commands only a service repo can run, and never told\n // a service repo that the work in front of it was its own.\n const report = await featureStatus(docsDir, feature, {\n service: opts.service,\n boundService: config.service,\n context,\n });\n if (opts.service !== undefined && !report.feature.services.includes(opts.service)) {\n fail(\n json,\n \"unknown-service\",\n `${feature.id} carries no delta for '${opts.service}'. It touches: ${\n report.feature.services.length > 0 ? report.feature.services.join(\", \") : \"(no services)\"\n }.`,\n );\n return;\n }\n const ambiguous = await featureCandidates(docsDir, featureArg, \"include\", context);\n if (json) {\n emitJson({ command: \"status\", docsDir, scope: \"feature\", ...featureJson(report) });\n } else {\n printFeature(report, ambiguous.length > 1 ? ambiguous.map((c) => c.dirName) : []);\n }\n } catch (err) {\n if (err instanceof DocsRepoUnavailableError) {\n reportDocsRepoError(json, err);\n return;\n }\n // One unreadable artifact must not escape as a stack trace and an\n // `internal` envelope that names nothing: the projection is\n // all-or-nothing (a status missing one artifact reads as a feature that\n // does not owe it), so the honest answer is a refusal naming the path.\n // The errno-vs-path reading this branch depends on — the reason one\n // directory where a file belongs used to kill BOTH forms of status —\n // now lives once, with its reasoning, on the shared helper.\n reportRepositoryUnavailable(\n json,\n err,\n `the status of ${scope} would be missing an artifact`,\n docsDir,\n );\n }\n // No exit code is set on any path above that succeeded, deliberately.\n // `doctor` exits 1 because a broken installation makes every later\n // command lie, and `validate` exits 1 because it IS the fleet gate. This\n // command is a question, and its answer is equally true whether the\n // feature is finished or not — an agent runs it precisely BECAUSE work is\n // outstanding, and exiting 1 there would make every `set -e` script and\n // every CI step read \"there is work to do\" as a failure.\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* JSON */\n/* ------------------------------------------------------------------ */\n"]}
@@ -1,9 +1,8 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { readFile } from "node:fs/promises";
3
3
  import { relative } from "node:path";
4
- import { loadConfig } from "../../core/envelope/config.js";
5
4
  import { decodeDocument, NotUtf8DocumentError } from "../../core/kernel/document-bytes.js";
6
- import { emitJson, fail, NO_SERVICE_MESSAGE, reportNoConfig } from "../../core/envelope/json.js";
5
+ import { configOrRefuse, emitJson, fail, NO_SERVICE_MESSAGE } from "../../core/envelope/json.js";
7
6
  import { SPEC_AXES } from "../../core/repo/paths.js";
8
7
  import { locateServicePaths, resolveServiceTarget } from "../../core/repo/service-target.js";
9
8
  import { parseRequirements } from "../../core/document/parse.js";
@@ -19,12 +18,9 @@ export function registerSteps(program) {
19
18
  .option("--json", "emit the machine contract instead of the human view")
20
19
  .action(async (opts) => {
21
20
  const json = opts.json === true;
22
- const loaded = await loadConfig();
23
- if (loaded.kind !== "loaded") {
24
- reportNoConfig(json, loaded);
21
+ const config = await configOrRefuse(json);
22
+ if (config === null)
25
23
  return;
26
- }
27
- const config = loaded.config;
28
24
  const named = opts.service ?? config.service;
29
25
  if (named === undefined)
30
26
  return fail(json, "invalid-option", NO_SERVICE_MESSAGE);