@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
package/WORKFLOW.md CHANGED
@@ -19,7 +19,7 @@ that order.
19
19
  [Why there is no task list](#why-there-is-no-task-list)
20
20
  - [Picking the work back up](#picking-the-work-back-up)
21
21
  - [Day zero: onboarding a fleet](#day-zero-onboarding-a-fleet) · [Command notes](#command-notes) ·
22
- [The MCP server](#the-mcp-server)
22
+ [The MCP server](#the-mcp-server) · [Build on the envelope](#build-on-the-envelope)
23
23
 
24
24
  ## The two flows
25
25
 
@@ -97,7 +97,10 @@ no fleet manifest, deliberately — the wiring is one file per repo, and it is r
97
97
  entry, in one journaled transaction, and guesses nothing: a human stated every fact in the file,
98
98
  which is what keeps seed on the same side of the no-extractor line as `loam new`. Re-running is
99
99
  safe — once the landscape has been hand-edited, seed refuses (`seed-landscape-edited`) rather
100
- than overwrite it.
100
+ than overwrite it. That makes seed a day-zero step: list every service id you already know in
101
+ `fleet.yaml` up front, because the first `metadata { op }` below has no spelling in
102
+ `fleet.yaml`. From then on the map grows without seed — `loam adopt --service <id>` briefs the
103
+ element and edges a new service adds, and a feature's C4 delta carries every other change.
101
104
 
102
105
  Drawing it by hand instead is entirely legal, and the landscape is a **required** artifact
103
106
  either way, not an optional one: `loam validate --all` reports `landscape.missing` as an error
@@ -113,11 +116,13 @@ no fleet manifest, deliberately — the wiring is one file per repo, and it is r
113
116
  repo:
114
117
 
115
118
  ```bash
116
- loam init --docs ../docs-repo --service payment-service # no --create: it JOINS
117
- loam doctor # confirms the wiring
119
+ loam init --docs ../docs-repo --service payment-service --agent-profile service # no --create: it JOINS
120
+ loam doctor # confirms the wiring
118
121
  ```
119
122
 
120
- `--service` is the binding: without it `loam vouch` and `loam gherkin` refuse outright, and
123
+ `--agent-profile service` writes the five workflows that run beside the code. Without it the
124
+ profile is `full`, which also writes `loam-feature` and `loam-ship` — the two workflows the docs
125
+ repo keeps — into the service checkout. `--service` is the binding: without it `loam vouch` and `loam gherkin` refuse outright, and
121
126
  `loam verify --service --record` / `--results` refuse to attest, because none of them will write
122
127
  a service's documents from a repo that has not said which service it is. Reading —
123
128
  `loam verify --service` on its own — needs no binding, because it writes nothing. Commit the
@@ -137,7 +142,8 @@ no fleet manifest, deliberately — the wiring is one file per repo, and it is r
137
142
  stamping the source and content digests that make later staleness detectable. `loam list --json`
138
143
  in the docs repo then grades the whole fleet on the presence ladder
139
144
  `empty → partial → documented → sourced → vouched`, and `loam list --needs-work` is the remaining
140
- worklist.
145
+ worklist — including any vouched service whose spec moved after the vouch (`vouched (stale)`,
146
+ missing a re-vouch), which `loam status` names as `next.revouch`.
141
147
 
142
148
  Steps 3–6 repeat per service and are independent — no ordering, no lock, no shared state between two
143
149
  services' runs — so ten services can be onboarded by ten people in parallel. The one file they all
@@ -155,9 +161,9 @@ configurable graph, because a governed system needs one lifecycle and one meanin
155
161
  | `delta.likec4` | feature | no | New elements and edges, each tagged with the feature id. The design. A requirements-only change deletes it |
156
162
  | `specs/<svc>/spec.md` | service | yes | The behaviour delta — `ADDED` / `MODIFIED` / `REMOVED` requirements, each with Given/When/Then scenarios |
157
163
  | `specs/<svc>/arch.spec.md` | service | no | The architectural obligations a business spec never mentions: outbox, retries, idempotency, alerts. Same grammar, plus `Covers:` lines tying each to the C4 elements it accounts for |
158
- | `specs/<svc>/openapi.yaml` | service | conditional | The contract delta. Required where the fleet map shows somebody calls this service |
159
- | `specs/<svc>/asyncapi.yaml` | service | no | The event-contract delta: a complete AsyncAPI 3.0 document restating the living contract around the changed slots. `loam rebase` pins restated slots (`x-loam-based-on`), `x-loam-remove: true` retires one |
160
- | `verification.yaml` | feature | yes | The done-check record: every claim, its answer, its evidence, and who gave it |
164
+ | `specs/<svc>/openapi.yaml` | service | conditional | The contract delta: only the operations the feature adds, changes or removes — an operation it leaves out stays as the living contract has it. Required where the fleet map shows somebody calls this service |
165
+ | `specs/<svc>/asyncapi.yaml` | service | no | The event-contract delta: an AsyncAPI 3.0 document holding only the slots the feature adds, changes or removes. `loam rebase` pins each one the living contract already has (`x-loam-based-on`), `x-loam-remove: true` retires one |
166
+ | `verification/<svc>.yaml` | feature | yes | The done-check record, one file per attesting service: every claim, its answer, its evidence, and who gave it. The all-at-once form writes the one file `verification.yaml` instead |
161
167
 
162
168
  Two fleet/living axes sit beside this feature graph. `services/<svc>/asyncapi.yaml` joins
163
169
  `publishes`/`consumes` edges to `Publishes:`/`Consumes:` requirements, and the event axis carries
@@ -175,7 +181,7 @@ present and fine. Marking everything required is how an axis that is optional ev
175
181
  reported as missing everywhere and then ignored.
176
182
 
177
183
  **Dependencies are enablers, not gates — with one real exception.** You can author these in any
178
- order. `verification.yaml` is the exception, and it is a genuine impossibility rather than a
184
+ order. The verification record is the exception, and it is a genuine impossibility rather than a
179
185
  preference: its checklist is *derived* from the delta and the per-service specs, so with none of
180
186
  them written there is no question set to answer. That artifact alone comes back `blocked`, naming
181
187
  what it is waiting on.
@@ -265,16 +271,39 @@ runs already recorded (the join partners' documentation state, staleness, the ve
265
271
  of the features touching the service, an interrupted docs commit). Its verdict is advice to a
266
272
  pipeline loam does not own — advisory by default, `--strict` the per-invocation CI lever — and it
267
273
  deliberately changes nothing about what gates the archive: verify still never gates the merge, and
268
- archive still reads no verification record.
274
+ archive still reads no verification record. Know what `--strict` buys before wiring it: its
275
+ freshness check reads `content.stale` over the gated service **and every documented join partner**,
276
+ so one partner whose spec moved under its vouch — every archive into a vouched spec does that —
277
+ turns this service's `gate --strict` red until that partner's owner re-vouches from their own repo.
278
+ That is the question asked honestly (a deploy joins documents nobody has re-read), but it is
279
+ another team's queue: a pipeline that must not wait on it runs `gate` without `--strict` and
280
+ branches on the `content.stale` findings in `checks[]` itself.
281
+
282
+ Which features it grades is the one thing a pipeline has to say. Without `--since`, the gate reads
283
+ **in-flight features only**: a feature leaves the check the moment it is archived, so a slice that
284
+ deploys after its feature shipped is gated without it — and under `--strict`, archiving would be
285
+ the cheapest way past an unverified-feature warning. `loam gate --since <ref>` closes that: pass
286
+ the docs-repo commit your last deploy recorded, and every feature archived after it (its
287
+ `features/archive/<FEAT>/` directory absent at the ref, or changed since — a feature unarchived,
288
+ edited and archived again ships again) that touches the service is graded too, under the same
289
+ `gate.feature-unverified`, its `features[]` row carrying `archived: true`. Its record is frozen, but
290
+ held to the checklist `loam archive` captured as the feature merged (`checklistAtArchive` on the
291
+ row): a record that had stopped answering the feature before it shipped reads `stale`, so archiving
292
+ is no longer a way past the warning. A feature archived by a loam that captured no checklist is read
293
+ as frozen history, and the row says so (`checklistAtArchive: null`). The ref is
294
+ read from the docs repo's own git exactly as `loam diff --base` reads its base, with the same
295
+ refusals when git cannot answer. loam stores nothing for it — the pipeline already knows which docs
296
+ commit it deployed from.
269
297
 
270
298
  **`loam diff` does not add one either — it is the review lens.** On a docs-repo PR,
271
299
  `loam diff --base origin/main` says what the branch *changes* in fleet-meaningful terms: services,
272
300
  requirements (by identity and content digest, so a rebase pin is not a change), operations,
273
- messages, and the cross-service joins between them — with the current consumers of every removal
274
- named, which is what a reviewer actually needs to know. `validate --all` remains the fleet gate;
275
- diff exits 1 on a removal the fleet still consumes (`diff.op-removed-consumed`,
276
- `diff.message-removed-consumed`) — and also whenever a document on either side could not be read,
277
- because a suspended axis is reported, never graded as "nothing changed" — so a PR job gets those
301
+ messages, and the cross-service joins between them — with the authored consumers of every removal,
302
+ deprecation and contract modification named, which is what a reviewer actually needs to know.
303
+ `validate --all` remains the fleet gate; diff exits 1 on a removal the fleet still consumes
304
+ (`diff.op-removed-consumed`, `diff.message-removed-consumed`) — and also whenever a document on
305
+ either side could not be read, because a suspended axis is reported, never graded as "nothing
306
+ changed" — so a PR job gets those
278
307
  checks for free, but a green diff is not a valid fleet: run both. For the byte-level OpenAPI
279
308
  breaking-change catalogue, use oasdiff on the two states of one contract; [the `loam diff` command
280
309
  note](#command-notes) below has the exact invocation, and loam deliberately does not reimplement
@@ -312,16 +341,16 @@ repository, while the runtime page cannot go out of date the same way.
312
341
 
313
342
  `AGENTS.md` — the one file `init` lays down that every host auto-loads, and so the first thing an
314
343
  agent reads — now works the same way, which changes the reading order. It is orientation only (the
315
- layout, the cycle, what gates and what only advises, at 8,762 bytes rather than 109,399), and the
316
- four **reference pages** it used to carry inline come out of the binary at the moment the question
317
- arises: `loam instructions loam-codes`, `loam-spine`, `loam-authoring` and `loam-done-check`, each
318
- taking no arguments and printing whole, listed beside the six workflows by a bare
319
- `loam instructions`. `init` writes no file for any of them, so nothing new landed in the repo.
344
+ layout, the cycle, what gates and what only advises, held under a 12,000-byte ceiling a test enforces
345
+ where it used to be 109,399 bytes), and the four **reference pages** it used to carry inline come
346
+ out of the binary at the moment the question arises: `loam instructions loam-codes`, `loam-spine`,
347
+ `loam-authoring` and `loam-done-check`, each taking no arguments and printing whole, listed beside
348
+ the six workflows by a bare `loam instructions`. `init` writes no file for any of them, so nothing new landed in the repo.
320
349
 
321
350
  | Workflow | When | What it drives |
322
351
  |---|---|---|
323
352
  | `loam-adopt` | Once per service, before any feature touches it | `loam adopt` briefs the baseline; an agent reads the code and writes it as `draft`; validate twice — the service, then the fleet — and hand to a human to vouch |
324
- | `loam-feature` | Starting a change | `loam explore` to decide what it touches, `loam new` to scaffold, author the delta and the specs, `loam rebase` to pin, `loam validate`, `loam dependencies` |
353
+ | `loam-feature` | Starting a change — after asking whether it needs a feature at all (step 0) | `loam explore` to decide what it touches, `loam new` to scaffold, author the delta and the specs, write what they leave open under `## Open questions` in `intent.md` (ungraded), `loam rebase` to pin, `loam validate`, `loam dependencies` |
325
354
  | `loam-implement` | Building one service's slice, in that service's repo | `loam delta` is the task, `loam gherkin` emits the acceptance criteria, step definitions come before code |
326
355
  | `loam-check` | Any time; always before shipping | `loam validate`, and fixing what it reports by code |
327
356
  | `loam-verify` | When the code is built | `loam verify` derives the checklist; the test runner answers what it can; you answer the rest with evidence |
@@ -371,6 +400,22 @@ this command just makes sure you are weighing all of it.
371
400
 
372
401
  ### The honestly-small change
373
402
 
403
+ **The smallest change needs no feature at all.** A change that touches no requirement, no operation
404
+ and no fleet edge — a refactor, a dependency bump, a fix that brings the code back to behaviour the
405
+ living spec already documents — changes nothing in the docs repo, so there is nothing to scaffold,
406
+ pin or archive. It is a code PR in the service repo, and then, in that repo:
407
+
408
+ ```bash
409
+ loam validate --service payment-service # the only place sources.stale is visible
410
+ loam vouch --pack --service payment-service # read-only: what the moved sources owe a re-read
411
+ ```
412
+
413
+ `sources.stale` names the paths that moved since the spec was last vouched, the pack is the reading
414
+ list, and the vouch itself is a person's. That is lighter than a feature by construction rather than
415
+ by exemption: there is no delta for a gate to grade, and the one signal that did move — the vouch —
416
+ is still owed. The moment the change alters what a requirement promises, it is a feature, however
417
+ small, and the lane below is the floor. `loam-feature`'s step 0 asks the same question first.
418
+
374
419
  Most changes are not cross-service features. A one-service, requirements-only change — a new
375
420
  refusal, a tightened invariant, behaviour behind an endpoint that already exists — walks the same
376
421
  graph with most of it not owed, and the tooling already says so. The whole loop:
@@ -405,15 +450,15 @@ Every command takes `--json`, and the contract is built so an agent never has to
405
450
  - **Findings carry stable codes** — `c4.valid`, `spine.op-undefined`, `coherence.ok`. Branch on the
406
451
  code. Prose gets reworded; codes do not.
407
452
  - **The protocol has a narrowed form, and one code is answerable as data** —
408
- `loam instructions loam-check` is 84,151 bytes, almost all of it the per-code fix tables, and
409
- `--no-fix-tables` prints the same page at 3,541 with each table's scope paragraph kept. Branch on
410
- the code, then `loam explain <code> --json` for the two or three a run reported (473 bytes each),
411
- or `loam explain --codes --json` once for the whole vocabulary — 293 finding codes and 46 refusal
412
- codes, 339 of the 390 loam emits. The four families no fix table grades (`doctor.*`, `next.*`,
413
- `diff.*`, `gate.*`) are among them, so a code read out of a `status`, `doctor`, `diff` or `gate`
414
- payload is answerable as data like any other; the 51 that are not are the OpenSpec migration
415
- surface (`openspec.*`, `mapping.*`), which an agent meets once before the repository is governed
416
- at all, and two `ok`-severity confirmations.
453
+ `loam instructions loam-check` is almost all per-code fix tables, and `--no-fix-tables` prints the
454
+ same page in a few kilobytes with each table's scope paragraph kept. Branch on the code, then
455
+ `loam explain <code> --json` for the two or three a run reported, or `loam explain --codes --json`
456
+ once for the whole vocabulary — every finding and refusal code `loam explain` answers. The four
457
+ families no fix table grades (`doctor.*`, `next.*`, `diff.*`, `gate.*`) are among them, so a code
458
+ read out of a `status`, `doctor`, `diff` or `gate` payload is answerable as data like any other;
459
+ the codes that are not are the OpenSpec migration surface (`openspec.*`, `mapping.*`), which an
460
+ agent meets once before the repository is governed at all, and a couple of `ok`-severity
461
+ confirmations.
417
462
  - **`validate --json` already carries the fix for every code it raised** — `fixes` is a map keyed by
418
463
  code, so joining `findings[].code` to `fixes[code]` answers "what now" out of the payload you are
419
464
  holding: no `loam explain` round trip per code, and no fix table loaded into context to read one.
@@ -481,10 +526,13 @@ A task list authored beside a spec is a second mutable state. It drifts — the
481
526
  checklist does not, and now two documents describe the work and only one of them is checked. The
482
527
  question a task list answers is a good one; the file is the wrong place to answer it.
483
528
 
484
- loam derives the answer instead, at two altitudes:
529
+ loam derives the answer instead, at three altitudes:
485
530
 
486
531
  - **What to do next**, from `loam status`: the artifact table plus an ordered `next[]`, recomputed
487
532
  every run, so it cannot disagree with the files.
533
+ - **Which promise to pick up, and what closes it**, from `loam tasks <FEAT>`: one unit per promise
534
+ the feature makes, each carrying the step it is waiting on and the fact that retires it. `--next`
535
+ narrows it to the single leaf packet an orchestrator hands one coding agent.
488
536
  - **What to build**, from `loam delta <FEAT> --service <svc>`: the intent, every requirement body
489
537
  and every Given/When/Then line reproduced verbatim, the endpoints added or retired, and the edges
490
538
  around it. That output *is* the coding task, and it is regenerated from the spec each time rather
@@ -501,6 +549,15 @@ input — a human's statement of the problem, which nothing can derive. The desi
501
549
  operations against the contracts. A prose design document beside those would be a third description
502
550
  of the same change, and the only one nothing grades.
503
551
 
552
+ The third altitude is new; the decision above is not. `loam tasks` is the `loam verify` checklist
553
+ read forwards — the same claims under the same ids, one unit per promise, each with the fact that
554
+ closes it — recomputed on every run from the delta, the generated suite and the verification record.
555
+ It is still not a file: nothing to tick, nothing to commit, nothing that can disagree with the
556
+ documents it was derived from, and so the reason there is no `tasks.md` is unchanged. A unit leaves
557
+ the list only when a confirmed claim in a non-stale verification record retires it, and an
558
+ agent-attested scenario stays on it at `attested` rather than being ticked off — the work list must
559
+ not be able to say more than the record does.
560
+
504
561
  ## Picking the work back up
505
562
 
506
563
  The three states worth naming, because each has one command:
@@ -577,10 +634,21 @@ replaces it with a `full` field naming the one run that carries them, which is 5
577
634
  (`rev-parse`/`ls-tree`/`show`, read-only, no checkout), so it refuses (`repository-unavailable`)
578
635
  when the docs repo has no history to ask and (`unknown-target`) when `--base` resolves to no commit
579
636
  there. What it reports is the *joined*, fleet-meaningful delta — a removed operation is graded
580
- against the landscape edges and living requirements that still name it (`diff.op-removed-consumed`,
581
- exit 1), a removed message against its `Consumes:` lines and consumes-edges — and an artifact
637
+ against the consumers the archive gate reads too ([SCHEMA.md](SCHEMA.md), "Who still consumes it"):
638
+ the edges of the `architecture/` project, other services' `Operations:` lines in either spec file
639
+ (not one naming its own service's operation of the same id) and the use-case hops attributed to it
640
+ (`diff.op-removed-consumed`, exit 1); a removed message against its `Consumes:` lines,
641
+ consumes-edges and hops — and an artifact
582
642
  unreadable on either side suspends that subject's axis and exits 1, because "nobody could look" must
583
- never read as "nothing changed". `validate --all` remains the fleet gate; diff is the review lens.
643
+ never read as "nothing changed". The exception is an `openapi.yaml` or `asyncapi.yaml` that cannot be
644
+ read but is byte-identical at the base (an AsyncAPI 2.x file nobody has converted yet): the branch did
645
+ not touch it, so it is skipped rather than failing every PR. A spec file stays suspended even then,
646
+ because another service's removal may be the one that could not be checked against it.
647
+ A landscape edge into the provider drawn with no `metadata { op }`
648
+ is the same doubt at warning strength: it is listed as a consumer nobody could check, never dropped.
649
+ An operation or message whose own node or `$ref` closure moved is `diff.op-modified` /
650
+ `diff.message-modified` — a warning that names the same consumers and does not say whether the
651
+ change breaks them. `validate --all` remains the fleet gate; diff is the review lens.
584
652
  For the byte-level OpenAPI breaking-change catalogue, run oasdiff on the two states of one contract:
585
653
  `git -C <docs> show <ref>:./services/<id>/openapi.yaml > base.yaml && oasdiff breaking base.yaml <docs>/services/<id>/openapi.yaml`
586
654
  (the `./` resolves the path relative to the docs directory rather than the git root, so this holds
@@ -594,7 +662,11 @@ landscape the branch changed since a base git ref of the docs repo — which is
594
662
  both passing on the first day of adoption and tightening after it, and which for that reason
595
663
  grades nothing outside the branch: the consumer whose living requirement still names an operation
596
664
  this branch removed is not in scope, so that finding is `loam diff`'s and never this run's, and a
597
- scope holding no target says it graded nothing rather than reporting the word valid.
665
+ scope holding no target says it graded nothing rather than reporting the word valid. The landscape
666
+ is graded by what in it the branch changed: a call edge the branch added or edited is checked
667
+ against its provider's contract even when the provider is out of scope, and the fleet-wide
668
+ vocabulary warnings (a permission, capability, obligation or glossary term nothing cites) and the
669
+ AGENTS.md stamp ride the run only when the file that carries them changed.
598
670
 
599
671
  **`loam gherkin`** — a feature's changed requirements, or (without a feature) the full living suite.
600
672
  Deterministic, digest-stamped, regeneration-owned; run in the service's own repo.
@@ -603,21 +675,45 @@ Deterministic, digest-stamped, regeneration-owned; run in the service's own repo
603
675
  on every operation in the contract delta that the living docs already hold; what this feature is
604
676
  *adding* has nothing to pin against, and the output says so per item. On the requirement axis that
605
677
  stops a second feature rewriting the same requirement from landing on top of the first in silence;
606
- on the contract axis it also marks the rest of the delta as **quotation** — an `openapi.yaml` is a
607
- complete document, so without the pin the merge upserts operations you only restated and reverts
608
- whatever landed on them. Restamping is the last step of resolving a collision, not the resolution —
609
- the output says which pins moved.
610
-
611
- **`loam verify`** — `--results <report.json>` answers the scenario claims from a cucumber JSON run,
678
+ on the contract axis it also tells an edit from a **quotation** — the merge upserts every operation
679
+ the delta spells, so without the pin an operation you only quoted reverts whatever landed on it.
680
+ The cheaper fix is not to quote: a feature's `openapi.yaml` or `asyncapi.yaml` holds only what it
681
+ adds, changes or removes, the merge leaves every operation it does not spell exactly as the living
682
+ contract has it, and a local `$ref` resolves into the living document — so an add-only contract
683
+ delta has nothing to pin and needs no rebase. Each quote costs a pin, and a pinned quote whose living
684
+ operation a parallel feature removes becomes `openapi.baseline-invalid`, an error that blocks the
685
+ archive until the quote is deleted, with `openapi.op-revived` beside it — while an unpinned one is
686
+ `openapi.op-revived` alone (`asyncapi.slot-revived` on the event axis), which gates the archive too,
687
+ because the merge would read the retired operation as an addition and put it back. Rebase pins neither as new: it prints
688
+ the same finding (`revived[]` in `--json`), and whether the quote goes or the feature re-adds the
689
+ operation on purpose is the author's call. It judges the delta as it will write it, pins included:
690
+ a quote repinned over whatever now lives at an address an archived feature moved the operation away
691
+ from becomes an edit the merge writes, and that revival is reported by the rebase (or `--dry-run`)
692
+ that makes it, not first by the next validate. Restamping is the last step of resolving a collision,
693
+ not the resolution — the output says which pins moved.
694
+
695
+ **`loam verify`** — `--results <report.json>` answers the scenario claims from a test run,
612
696
  digest-matched, so only a green run confirms one, and the record writes down which file it read
613
- (path, sha256, mtime). It answers *only* those claims, so on a feature with open `service.exists` /
614
- `api.exposes` claims it is passed alongside `--record` or refused (`answers-mismatch`).
615
- `--record <answers.json>` takes the agent's answers, refusing anything unevidenced — under
616
- `--service`, refusing a `file:line` that does not resolve in that repo at the attested commit — and
617
- a scenario claim answered that way makes the record `attested` rather than `verified`. A
618
- cross-service feature is recorded **once per service repo** with `--service <id>`, each run adding
619
- its own commit-bound attestation to one shared record; the `--service`-less `--record` form writes
620
- the whole record from one place and is refused (`record-federated`) once anyone else has attested.
697
+ (path, sha256, mtime). It reads two shapes: a cucumber JSON report (cucumber-js, cucumber-jvm,
698
+ behave, SpecFlow/Reqnroll, pytest-bdd `--cucumberjson` and godog's cucumber formatter all write
699
+ one), in which a matched scenario must also have run exactly the stamped scenario's number of
700
+ steps — a hand-tagged stub is not that scenario's run — or loam's runner-neutral
701
+ `{"loamScenarioReport": 1, "results": [{"digest": "…", "status": "passed"}]}`, which a JUnit,
702
+ pytest, Vitest or house runner's report is adapted into. Anything else, JUnit XML included, is
703
+ refused naming both (`answers-unreadable`). It answers *only* those claims, so on a feature with
704
+ open `service.exists` / `api.exposes` claims it is passed alongside `--record` or refused
705
+ (`answers-mismatch`). `--record <answers.json>` takes the agent's answers, refusing anything
706
+ unevidenced — a `file:line` that does not resolve to a real line in this repository, and under
707
+ `--service` one that does not resolve at the attested commit — and a scenario claim answered that
708
+ way makes the record `attested` rather than `verified`. A
709
+ cross-service feature is recorded **once per service repo** with `--service <id>`, each run writing
710
+ its own commit-bound attestation to a file of its own, `features/<FEAT>/verification/<id>.yaml` —
711
+ the directory is the record, so two teams' attestations on two docs-repo branches merge cleanly. The
712
+ `--service`-less `--record` form writes the whole record from one place, into the one file
713
+ `verification.yaml`, and is refused (`record-federated`) once anyone else has attested. A record
714
+ file that will not read (`record-unreadable`, git conflict markers included) is named with its safe
715
+ repair: a service's own file may be deleted and recorded again by that service; the one-file
716
+ `verification.yaml` may hold other services' attestations and is restored, never deleted.
621
717
 
622
718
  **`loam unarchive`** — it refuses rather than guesses: `snapshot-stale` when a merged file moved
623
719
  since (`--force` says that was meant), and `snapshot-corrupt` when a pre-image no longer hashes to
@@ -649,8 +745,8 @@ archive is undone, an interrupted unarchive is *finished*, because the merged te
649
745
  is written down nowhere else — or refusing with `commit-interrupted` when a file has been edited
650
746
  since. `loam doctor` reports that state rather than calling the repo healthy.
651
747
 
652
- The gate itself comes in two kinds. Coherence, an unknown service and the OpenAPI plan are judgement
653
- calls, and `--approve` carries them; two refusals are mechanical loss and `--approve` deliberately
748
+ The gate itself comes in two kinds. Coherence (a revived retired operation among it), an unknown
749
+ service and the OpenAPI plan are judgement calls, and `--approve` carries them; two refusals are mechanical loss and `--approve` deliberately
654
750
  does not — a living document still holding git conflict markers (`merge-failed`) and a living
655
751
  requirement that has strayed outside `## Requirements` (`living-outside-requirements`). loam's docs
656
752
  files must be **UTF-8** for the same reason: undecodable bytes refuse the merge (`merge-failed`,
@@ -666,19 +762,37 @@ beside the methods, addresses no operation and retires nothing; archive refuses
666
762
  (`openapi.remove-marker-path-level`) rather than publishing a meaningless key into the fleet's
667
763
  living contract. Validation refuses stale, mismatched, or unjustified markers; archive deletes the
668
764
  method without persisting the marker or garbage-collecting components, and reports the removals in
669
- both text and JSON. See [SCHEMA.md](SCHEMA.md) for the lifecycle contract.
765
+ both text and JSON. See [SCHEMA.md](SCHEMA.md) for the lifecycle contract. A delta that defines
766
+ another operationId where a living operation lives replaces it there, and when the merged contract
767
+ then defines it nowhere the gate grades it like a retirement: `openapi.remove-op-consumed` while an
768
+ edge of the `architecture/` project, another service's requirement, a use-case hop, or the
769
+ provider's own requirement as the merge would leave it still names the old id — so a rename redraws
770
+ the edge and restates its requirement in the same change, or defines the old operation at another
771
+ address if it is to stay. Every consumer is read after the feature's merge, so retiring one in the
772
+ same feature (MODIFY or REMOVE the requirement that names it) is a fix the gate sees.
773
+
774
+ A retirement also stays retired. The archive gate reads the removal markers every feature under
775
+ `features/archive/` left behind, and refuses a delta that spells an HTTP operation, a channel, an
776
+ event operation or a message one of them retired while the living contract no longer holds it —
777
+ or spells one at an address one of them retired it from or moved it away from while it lives on
778
+ elsewhere, which would put it back beside where it lives, judged on the contract the merge would
779
+ write (`openapi.op-revived`, `asyncapi.slot-revived` — warnings that gate). Without it, a parallel
780
+ feature that had merely quoted the operation archived second and put it back at exit 0.
781
+ `--approve` re-adds it on purpose, and the archive output names what came back and whose
782
+ retirement it reverses. A removal made by hand-editing a living contract, or an archive deleted
783
+ from disk, leaves no marker, and the gate cannot see it.
670
784
 
671
785
  ## The MCP server
672
786
 
673
- For hosts that reach tools through MCP rather than a shell, `loam mcp` serves the fourteen read
674
- commands — `validate`, `status`, `list`, `show`, `delta`, `explore`, `dependencies`, `diff`,
675
- `doctor`, `context`, `gate`, `steps`, `explain`, `instructions` — as MCP tools (`loam_validate` …
676
- `loam_instructions`) over stdio, newline-delimited JSON-RPC 2.0, protocol revision 2025-06-18 (older
677
- revisions negotiated down; JSON-RPC batch arrays refused). Every tool is advertised
678
- `readOnlyHint: true` and `openWorldHint: false`, which is what a host needs to run one without
679
- stopping to ask its user: the first says the call cannot change the repository, the second that it
680
- cannot reach anything outside the directory the server was launched in — and both are true of the
681
- whole base table only because the writing commands are kept out of it. It also publishes
787
+ For hosts that reach tools through MCP rather than a shell, `loam mcp` serves the sixteen read
788
+ commands — `validate`, `status`, `list`, `show`, `delta`, `tasks`, `explore`, `dependencies`,
789
+ `diff`, `doctor`, `context`, `adopt`, `gate`, `steps`, `explain`, `instructions` — as MCP tools
790
+ (`loam_validate` … `loam_instructions`) over stdio, newline-delimited JSON-RPC 2.0, protocol
791
+ revision 2025-06-18 (older revisions negotiated down; JSON-RPC batch arrays refused). Every tool is
792
+ advertised `readOnlyHint: true` and `openWorldHint: false`, which is what a host needs to run one
793
+ without stopping to ask its user: the first says the call cannot change the repository, the second
794
+ that it cannot reach anything outside the directory the server was launched in — and both are true
795
+ of the whole base table only because the writing commands are kept out of it. It also publishes
682
796
  `loam://orientation` and every `loam://instructions/<name>` page as Markdown resources, so the host
683
797
  can load version-matched guidance progressively; `loam://instructions/loam-check/compact` omits the
684
798
  large fix table and pairs with `loam explain <code>`. A typical host entry:
@@ -712,3 +826,99 @@ gets coherent `no-config` envelopes, not silence. Calls run strictly one at a ti
712
826
  `loam_validate` blocks the queue for its duration; MCP requests carry ids and hosts tolerate the
713
827
  latency. Even author mode excludes `vouch`, verification recording and a committing archive:
714
828
  human trust and irreversible lifecycle transitions stay under the CLI permission flow.
829
+
830
+ ## Build on the envelope
831
+
832
+ loam makes no network call, so it syncs with no tracker, dashboard or catalog — and does not need
833
+ to. Every command's `--json` is a stable contract, and the integrations are a few lines of shell
834
+ over it, owned by whoever runs them. Four recipes, each run against `examples/docs`.
835
+
836
+ **Feature work into a tracker.** `loam tasks <FEAT> --json` lists every open unit of a feature:
837
+ `id`, `subject` (the owning service), `claim`, `done` (its `check` is the fact that closes it) and
838
+ `dependsOn` (unit ids to finish first). The unit id is the idempotency key — prefixed with the
839
+ feature id, so one tracker holds every feature's units and each title says which — and a re-run
840
+ opens issues only for units it has not seen:
841
+
842
+ ```bash
843
+ existing=$(gh issue list --state all --limit 1000 --json title --jq '.[].title')
844
+ loam tasks FEAT-101 --json \
845
+ | jq -r '.feature as $f | .units[] | ["[\($f) \(.id)]", .subject, .claim, .done.check, (.dependsOn | map("[\($f) \(.)]") | join(" "))] | @tsv' \
846
+ | while IFS=$'\t' read -r key svc claim check after; do
847
+ grep -qF "$key" <<<"$existing" && continue
848
+ gh issue create --title "$key $claim" --body "Service: $svc. Done when: $check. After: ${after:-nothing}"
849
+ done
850
+ ```
851
+
852
+ On the example's FEAT-101 that is nineteen issues the first time and none the second. A finished
853
+ unit leaves `units[]` once the evidence its `check` names is recorded, so the issues to close are a
854
+ set difference against the last export. **A unit's id is a hash of what it says**, which is what
855
+ makes it a key and also its one hazard: rewording a scenario — its name or any Given/When/Then line
856
+ — or renaming a requirement that carries no `Requirement-ID:` mints a new id. The next export opens
857
+ a fresh issue, and the old one is orphaned: it drops out of `units[]` exactly as a finished unit
858
+ does, so read the set difference before closing anything as done, and carry the orphan's history
859
+ over by hand. Renaming a requirement that does carry a `Requirement-ID:` keeps every id. The same
860
+ loop drives `jira` or any CLI that creates an issue from a title and a body.
861
+
862
+ **The fleet view of every service repo's CI.** Staleness is visible only from each service repo,
863
+ so each one's CI publishes the per-service summary (SCHEMA.md, "The per-service summary a CI
864
+ pipeline publishes") wherever your fleet view reads. Rolling those up is a join against the list of
865
+ services the docs repo knows, so a service that never reported reads as unknown and never as clean:
866
+
867
+ ```bash
868
+ jq -s 'map({(.service): .}) | add // {}' summaries/*.json > reports.json
869
+ loam list --json | jq -r --slurpfile r reports.json '
870
+ .services[] | .id as $id | $r[0][$id] as $s
871
+ | [$id,
872
+ if $s == null then "unknown: no report"
873
+ elif now - ($s.generatedAt | fromdateiso8601) > 86400 then "unknown: last report \($s.generatedAt)"
874
+ elif $s.valid | not then "invalid: \($s.errors) error(s)"
875
+ elif $s.sources.stale + $s.contentStale > 0 then "owes a re-vouch: \($s.sources.stale) source-stale, \($s.contentStale) content-stale"
876
+ else "clean" end]
877
+ | @tsv'
878
+ ```
879
+
880
+ The day-old cutoff is a policy and yours to set; what is not a policy is that `generatedAt` comes
881
+ from the CI's clock, never loam's, so an old one means that repo stopped reporting.
882
+
883
+ **Which services must be up for suite X.** A fleet usually keeps that answer by hand — a compose
884
+ file, a CI matrix, a paragraph in a runbook — and it drifts from the journeys it is for. Tag each
885
+ cross-service use case with the suites it runs in (`#suite-smoke` on its `dynamic view`, declared in
886
+ the landscape's `specification`; one journey may carry several) and `loam list suites <name>`
887
+ derives it: the union of the suite's participants, resolved to service ids through the same join the
888
+ step grades use. The participants no service stands for are listed beside them rather than dropped —
889
+ the person who drives the flow, an `#external` topic or partner — because a stand that comes up
890
+ short fails its journey against something nobody was told to start:
891
+
892
+ ```bash
893
+ loam list suites smoke --json | jq -r '
894
+ if .useCases.unreadable then error("architecture/ did not parse: \(.useCases.error)") else . end
895
+ | .suites[0] | (.services[] | "up \(.)"), (.unresolved[] | "stand-in \(.element)\(if .external then " (#external)" else "" end)")'
896
+ ```
897
+
898
+ On the example that is three services up — checkout-web, notification-service, order-service — and
899
+ two stand-ins: `customer`, and `kafka.orderEvents`, `#external` through its kind. Tagging a journey
900
+ also opts it in to grading, so a suite cannot list a hop the map does not declare
901
+ (`usecase.step-unbacked`); `ungrouped[]` names the use cases in no suite, and an unknown suite name
902
+ refuses `unknown-target` rather than answering an empty environment.
903
+
904
+ **LikeC4's own MCP server beside loam's.** The fleet map is plain LikeC4, so LikeC4's MCP server
905
+ runs on the same docs repo with no configuration — `npx likec4 mcp --stdio .` from the docs root
906
+ exposed eighteen tools on the example, measured at the 1.59.2 pin loam carries: element, view and
907
+ relationship reads, graph traversals, metadata and tag queries. Run the likec4 loam itself depends
908
+ on (`npm ls likec4` names it), so the server parses the grammar loam validates. The two answer
909
+ different questions: `loam mcp` the joins, gates and lifecycle state; `likec4 mcp` the model as a
910
+ graph. One host entry carries both:
911
+
912
+ ```json
913
+ {
914
+ "mcpServers": {
915
+ "loam": { "command": "loam", "args": ["mcp"] },
916
+ "likec4": { "command": "npx", "args": ["likec4", "mcp", "--stdio", "."] }
917
+ }
918
+ }
919
+ ```
920
+
921
+ Two differences to know. LikeC4's server reads the root project, which excludes `features/**`, so
922
+ it sees the living map and never an in-flight delta — ask `loam show <FEAT> --json` for those. And
923
+ seventeen of its tools advertise `readOnlyHint`; `apply-semantic-layout` does not, so a host will
924
+ ask before running it.
package/dist/cli.js CHANGED
@@ -11,6 +11,7 @@ import { registerShow } from "./commands/show/show.js";
11
11
  import { registerStatus } from "./commands/status/status.js";
12
12
  import { registerSubsystem } from "./commands/subsystem/subsystem.js";
13
13
  import { registerDelta } from "./commands/delta/delta.js";
14
+ import { registerTasks } from "./commands/tasks/tasks.js";
14
15
  import { registerDiff } from "./commands/diff/diff.js";
15
16
  import { registerGherkin } from "./commands/gherkin/gherkin.js";
16
17
  import { registerRebase } from "./commands/rebase/rebase.js";
@@ -30,7 +31,7 @@ import { registerOpen } from "./commands/open.js";
30
31
  import { registerInstructions } from "./commands/instructions.js";
31
32
  import { registerExplain } from "./commands/explain/explain.js";
32
33
  import { registerMigrateOpenSpec } from "./commands/migrate-openspec/migrate-openspec.js";
33
- import { emitJsonError } from "./core/envelope/json.js";
34
+ import { emitJsonError, sayExplain } from "./core/envelope/json.js";
34
35
  import { LOAM_VERSION } from "./core/envelope/version.js";
35
36
  /**
36
37
  * The whole CLI surface, as a program nobody has parsed yet.
@@ -69,6 +70,7 @@ export function buildProgram() {
69
70
  registerStatus(program);
70
71
  registerSubsystem(program);
71
72
  registerDelta(program);
73
+ registerTasks(program);
72
74
  registerDiff(program);
73
75
  registerGherkin(program);
74
76
  registerRebase(program);
@@ -93,10 +95,10 @@ export function buildProgram() {
93
95
  * The two lines under the command list: where to start, and what to run when
94
96
  * lost.
95
97
  *
96
- * {@link HELP_GROUPS} does nearly all of the orienting work — twenty-nine
98
+ * {@link HELP_GROUPS} does nearly all of the orienting work — thirty
97
99
  * commands under seven headings, in the order of the work — but it stops one
98
100
  * step short of the two facts a newcomer does not have. Nothing on the page
99
- * said that `init` must run before any of the other twenty-eight, and nothing
101
+ * said that `init` must run before any of the others, and nothing
100
102
  * named the command that answers "what now"; the last thing a first
101
103
  * `loam --help` printed was `help [command]`.
102
104
  *
@@ -123,7 +125,7 @@ export const HELP_EPILOG = [
123
125
  * protocols (`loam instructions`) rather than inventing a second taxonomy, so a
124
126
  * reader who has met one has met the other.
125
127
  *
126
- * Twenty-nine commands printed as one flat registration-ordered list tells a
128
+ * Thirty commands printed as one flat registration-ordered list tells a
127
129
  * new reader nothing about which four to run first. NOTHING IS HIDDEN — a
128
130
  * hidden command is a command an agent cannot discover, and every one of these
129
131
  * still parses, still appears, and still carries the same flags. This is
@@ -135,7 +137,7 @@ const HELP_GROUPS = {
135
137
  explore: "Read the fleet", context: "Read the fleet", dependencies: "Read the fleet",
136
138
  explain: "Read the fleet", instructions: "Read the fleet", steps: "Read the fleet",
137
139
  adopt: "Adopt what exists", vouch: "Adopt what exists",
138
- new: "Change it", delta: "Change it", gherkin: "Change it", rebase: "Change it",
140
+ new: "Change it", delta: "Change it", tasks: "Change it", gherkin: "Change it", rebase: "Change it",
139
141
  subsystem: "Change it",
140
142
  validate: "Check it", diff: "Check it", verify: "Check it", gate: "Check it",
141
143
  archive: "Ship it", unarchive: "Ship it",
@@ -144,7 +146,7 @@ const HELP_GROUPS = {
144
146
  /**
145
147
  * Apply {@link HELP_GROUPS}, and FAIL CLOSED on a command it does not name.
146
148
  *
147
- * The throw is the point. A thirtieth command added without a heading would
149
+ * The throw is the point. A thirty-first command added without a heading would
148
150
  * otherwise land silently in an "unclassified" bucket at the bottom of the
149
151
  * page, which is the drift this whole file's neighbours are written to prevent;
150
152
  * `test/help-groups.test.ts` catches it at gate time, and this catches it the
@@ -162,6 +164,19 @@ function groupCommands(program) {
162
164
  }
163
165
  /** CommanderError codes whose output (already printed) is the point, not a failure to wrap. */
164
166
  const PASS_THROUGH = new Set(["commander.help", "commander.helpDisplayed", "commander.version"]);
167
+ /**
168
+ * What a text-mode usage error prints under commander's own diagnostic: the
169
+ * fix and the stable code, the two lines every other refusal carries through
170
+ * `fail`. Without them `loam diff` with no `--base` printed commander's one
171
+ * sentence and nothing else — the only refusals in the CLI a person could not
172
+ * look up, while `--json` already reported them as `invalid-option`.
173
+ */
174
+ function sayUsageFix(program) {
175
+ const name = process.argv.slice(2).find((token) => program.commands.some((c) => c.name() === token));
176
+ const help = name === undefined ? "`loam --help` lists the commands" : `\`loam ${name} --help\` lists the arguments and options it takes`;
177
+ console.error(`Fix the invocation: ${help}.`);
178
+ sayExplain("invalid-option");
179
+ }
165
180
  function main() {
166
181
  const program = buildProgram();
167
182
  // Set once a command's action actually runs: every subcommand declares its own
@@ -194,8 +209,11 @@ function main() {
194
209
  // Anything else is a usage error (unknown option/command, missing or
195
210
  // invalid argument). Commander has already printed its diagnostic to
196
211
  // stderr, and that line stays: the invariant below is about stdout only.
197
- if (!PASS_THROUGH.has(err.code) && jsonRequested()) {
198
- emitJsonError("invalid-option", err.message.replace(/^error: /, ""));
212
+ if (!PASS_THROUGH.has(err.code)) {
213
+ if (jsonRequested())
214
+ emitJsonError("invalid-option", err.message.replace(/^error: /, ""));
215
+ else
216
+ sayUsageFix(program);
199
217
  }
200
218
  process.exitCode = err.exitCode;
201
219
  return;