@ybotok/loam 0.2.0-alpha.6 → 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 (691) hide show
  1. package/CHANGELOG.md +1477 -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 +108 -11
  8. package/SCHEMA.md +721 -130
  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 +82 -33
  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 +55 -26
  116. package/dist/commands/seed/commit.js.map +1 -1
  117. package/dist/commands/seed/plan.js +64 -13
  118. package/dist/commands/seed/plan.js.map +1 -1
  119. package/dist/commands/seed/seed.js +6 -9
  120. package/dist/commands/seed/seed.js.map +1 -1
  121. package/dist/commands/show/show.js +3 -7
  122. package/dist/commands/show/show.js.map +1 -1
  123. package/dist/commands/status/print.js +13 -4
  124. package/dist/commands/status/print.js.map +1 -1
  125. package/dist/commands/status/status.js +3 -7
  126. package/dist/commands/status/status.js.map +1 -1
  127. package/dist/commands/steps/steps.js +3 -7
  128. package/dist/commands/steps/steps.js.map +1 -1
  129. package/dist/commands/subsystem/edit.js +7 -7
  130. package/dist/commands/subsystem/edit.js.map +1 -1
  131. package/dist/commands/subsystem/move.js +60 -25
  132. package/dist/commands/subsystem/move.js.map +1 -1
  133. package/dist/commands/subsystem/query.js +2 -4
  134. package/dist/commands/subsystem/query.js.map +1 -1
  135. package/dist/commands/subsystem/subsystem.js +4 -7
  136. package/dist/commands/subsystem/subsystem.js.map +1 -1
  137. package/dist/commands/subsystem/sync.js +141 -71
  138. package/dist/commands/subsystem/sync.js.map +1 -1
  139. package/dist/commands/subsystem/txn/report.js +55 -5
  140. package/dist/commands/subsystem/txn/report.js.map +1 -1
  141. package/dist/commands/subsystem/txn/txn.js +121 -45
  142. package/dist/commands/subsystem/txn/txn.js.map +1 -1
  143. package/dist/commands/subsystem/txn/views.js +82 -12
  144. package/dist/commands/subsystem/txn/views.js.map +1 -1
  145. package/dist/commands/tasks/render.js +116 -0
  146. package/dist/commands/tasks/render.js.map +1 -0
  147. package/dist/commands/tasks/tasks.js +113 -0
  148. package/dist/commands/tasks/tasks.js.map +1 -0
  149. package/dist/commands/unarchive/manifest.js +6 -9
  150. package/dist/commands/unarchive/manifest.js.map +1 -1
  151. package/dist/commands/unarchive/restore.js +17 -28
  152. package/dist/commands/unarchive/restore.js.map +1 -1
  153. package/dist/commands/unarchive/unarchive.js +22 -30
  154. package/dist/commands/unarchive/unarchive.js.map +1 -1
  155. package/dist/commands/validate/arch-coverage.js +6 -15
  156. package/dist/commands/validate/arch-coverage.js.map +1 -1
  157. package/dist/commands/validate/checks/fleet-shape.js +4 -49
  158. package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
  159. package/dist/commands/validate/checks/health-deps.js +9 -6
  160. package/dist/commands/validate/checks/health-deps.js.map +1 -1
  161. package/dist/commands/validate/checks/requirements.js +18 -8
  162. package/dist/commands/validate/checks/requirements.js.map +1 -1
  163. package/dist/commands/validate/checks/sources.js +6 -2
  164. package/dist/commands/validate/checks/sources.js.map +1 -1
  165. package/dist/commands/validate/feature.js +23 -19
  166. package/dist/commands/validate/feature.js.map +1 -1
  167. package/dist/commands/validate/fleet/census.js +26 -3
  168. package/dist/commands/validate/fleet/census.js.map +1 -1
  169. package/dist/commands/validate/fleet/landscape.js +35 -32
  170. package/dist/commands/validate/fleet/landscape.js.map +1 -1
  171. package/dist/commands/validate/fleet/map/attest.js +7 -12
  172. package/dist/commands/validate/fleet/map/attest.js.map +1 -1
  173. package/dist/commands/validate/fleet/map/bindings.js +16 -5
  174. package/dist/commands/validate/fleet/map/bindings.js.map +1 -1
  175. package/dist/commands/validate/fleet/map/consumers.js +43 -122
  176. package/dist/commands/validate/fleet/map/consumers.js.map +1 -1
  177. package/dist/commands/validate/fleet/map/usecases.js +5 -11
  178. package/dist/commands/validate/fleet/map/usecases.js.map +1 -1
  179. package/dist/commands/validate/fleet/obligations.js +1 -5
  180. package/dist/commands/validate/fleet/obligations.js.map +1 -1
  181. package/dist/commands/validate/fleet/scorecard/print.js +5 -1
  182. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
  183. package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
  184. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
  185. package/dist/commands/validate/fleet/usecases/usecases.js +6 -4
  186. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -1
  187. package/dist/commands/validate/fleet/views/fleet-project.js +1 -1
  188. package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -1
  189. package/dist/commands/validate/fleet/views/ids.js +33 -7
  190. package/dist/commands/validate/fleet/views/ids.js.map +1 -1
  191. package/dist/commands/validate/fleet/views/root-documents.js +17 -35
  192. package/dist/commands/validate/fleet/views/root-documents.js.map +1 -1
  193. package/dist/commands/validate/fleet/views/stale.js +31 -14
  194. package/dist/commands/validate/fleet/views/stale.js.map +1 -1
  195. package/dist/commands/validate/links/corpus.js +1 -4
  196. package/dist/commands/validate/links/corpus.js.map +1 -1
  197. package/dist/commands/validate/links/glossary.js.map +1 -1
  198. package/dist/commands/validate/report.js +5 -4
  199. package/dist/commands/validate/report.js.map +1 -1
  200. package/dist/commands/validate/scope.js +113 -0
  201. package/dist/commands/validate/scope.js.map +1 -0
  202. package/dist/commands/validate/service/api.js +16 -104
  203. package/dist/commands/validate/service/api.js.map +1 -1
  204. package/dist/commands/validate/service/contracts/generated.js +134 -0
  205. package/dist/commands/validate/service/contracts/generated.js.map +1 -0
  206. package/dist/commands/validate/service/events/events.js +1 -1
  207. package/dist/commands/validate/service/events/events.js.map +1 -1
  208. package/dist/commands/validate/service/events/message-producers.js +1 -1
  209. package/dist/commands/validate/service/events/message-producers.js.map +1 -1
  210. package/dist/commands/validate/service/evidence-pins.js +56 -10
  211. package/dist/commands/validate/service/evidence-pins.js.map +1 -1
  212. package/dist/commands/validate/service/service.js +16 -26
  213. package/dist/commands/validate/service/service.js.map +1 -1
  214. package/dist/commands/validate/service/specs.js +31 -38
  215. package/dist/commands/validate/service/specs.js.map +1 -1
  216. package/dist/commands/validate/service/spine.js +8 -11
  217. package/dist/commands/validate/service/spine.js.map +1 -1
  218. package/dist/commands/validate/service/usecases/usecases.js +3 -3
  219. package/dist/commands/validate/service/usecases/usecases.js.map +1 -1
  220. package/dist/commands/validate/validate.js +44 -26
  221. package/dist/commands/validate/validate.js.map +1 -1
  222. package/dist/commands/verify/cross/diff.js +2 -6
  223. package/dist/commands/verify/cross/diff.js.map +1 -1
  224. package/dist/commands/verify/evidence/contract.js +1 -1
  225. package/dist/commands/verify/evidence/contract.js.map +1 -1
  226. package/dist/commands/verify/evidence/read.js +12 -3
  227. package/dist/commands/verify/evidence/read.js.map +1 -1
  228. package/dist/commands/verify/frozen.js +45 -23
  229. package/dist/commands/verify/frozen.js.map +1 -1
  230. package/dist/commands/verify/record.js +69 -17
  231. package/dist/commands/verify/record.js.map +1 -1
  232. package/dist/commands/verify/report.js +27 -44
  233. package/dist/commands/verify/report.js.map +1 -1
  234. package/dist/commands/verify/results.js +112 -30
  235. package/dist/commands/verify/results.js.map +1 -1
  236. package/dist/commands/verify/verify.js +54 -51
  237. package/dist/commands/verify/verify.js.map +1 -1
  238. package/dist/commands/vouch/pack/pack.js +2 -2
  239. package/dist/commands/vouch/pack/pack.js.map +1 -1
  240. package/dist/commands/vouch/pack/print.js +1 -6
  241. package/dist/commands/vouch/pack/print.js.map +1 -1
  242. package/dist/commands/vouch/pack/sections.js +1 -2
  243. package/dist/commands/vouch/pack/sections.js.map +1 -1
  244. package/dist/commands/vouch/run.js +26 -81
  245. package/dist/commands/vouch/run.js.map +1 -1
  246. package/dist/commands/vouch/sample/plan.js +20 -34
  247. package/dist/commands/vouch/sample/plan.js.map +1 -1
  248. package/dist/commands/vouch/vet/verify.js +39 -7
  249. package/dist/commands/vouch/vet/verify.js.map +1 -1
  250. package/dist/commands/vouch/vouch.js +10 -17
  251. package/dist/commands/vouch/vouch.js.map +1 -1
  252. package/dist/core/agent/agents-md/command-map.js +57 -102
  253. package/dist/core/agent/agents-md/command-map.js.map +1 -1
  254. package/dist/core/agent/agents-md/map/explain.js +4 -3
  255. package/dist/core/agent/agents-md/map/explain.js.map +1 -1
  256. package/dist/core/agent/agents-md/map/lenses/context.js +4 -3
  257. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
  258. package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
  259. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
  260. package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
  261. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
  262. package/dist/core/agent/agents-md/map/mcp.js +10 -7
  263. package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
  264. package/dist/core/agent/agents-md/map/status.js +9 -3
  265. package/dist/core/agent/agents-md/map/status.js.map +1 -1
  266. package/dist/core/agent/agents-md/map/subsystem.js +31 -17
  267. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
  268. package/dist/core/agent/agents-md/map/usecases.js +24 -6
  269. package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
  270. package/dist/core/agent/agents-md/refusals.js +37 -21
  271. package/dist/core/agent/agents-md/refusals.js.map +1 -1
  272. package/dist/core/agent/agents-md.js +25 -10
  273. package/dist/core/agent/agents-md.js.map +1 -1
  274. package/dist/core/agent/agents-stamp.js +10 -0
  275. package/dist/core/agent/agents-stamp.js.map +1 -1
  276. package/dist/core/agent/scaffold.js +10 -15
  277. package/dist/core/agent/scaffold.js.map +1 -1
  278. package/dist/core/agent/tools/dialects.js +1 -0
  279. package/dist/core/agent/tools/dialects.js.map +1 -1
  280. package/dist/core/agent/tools/registry.js +41 -76
  281. package/dist/core/agent/tools/registry.js.map +1 -1
  282. package/dist/core/agent/workflows/adopt.js +5 -3
  283. package/dist/core/agent/workflows/adopt.js.map +1 -1
  284. package/dist/core/agent/workflows/check.js +25 -22
  285. package/dist/core/agent/workflows/check.js.map +1 -1
  286. package/dist/core/agent/workflows/closing.js +54 -21
  287. package/dist/core/agent/workflows/closing.js.map +1 -1
  288. package/dist/core/agent/workflows/feature.js +79 -11
  289. package/dist/core/agent/workflows/feature.js.map +1 -1
  290. package/dist/core/agent/workflows/implement.js +91 -13
  291. package/dist/core/agent/workflows/implement.js.map +1 -1
  292. package/dist/core/agent/workflows/reference/authoring.js +3 -1
  293. package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
  294. package/dist/core/agent/workflows/reference/codes.js +8 -6
  295. package/dist/core/agent/workflows/reference/codes.js.map +1 -1
  296. package/dist/core/agent/workflows/reference/done-check.js +37 -15
  297. package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
  298. package/dist/core/agent/workflows/reference/reference.js.map +1 -1
  299. package/dist/core/agent/workflows/reference/spine.js +62 -24
  300. package/dist/core/agent/workflows/reference/spine.js.map +1 -1
  301. package/dist/core/asyncapi/baseline/plan.js +14 -81
  302. package/dist/core/asyncapi/baseline/plan.js.map +1 -1
  303. package/dist/core/asyncapi/digest.js +30 -25
  304. package/dist/core/asyncapi/digest.js.map +1 -1
  305. package/dist/core/asyncapi/merge/components.js +5 -4
  306. package/dist/core/asyncapi/merge/components.js.map +1 -1
  307. package/dist/core/asyncapi/merge/error.js +7 -2
  308. package/dist/core/asyncapi/merge/error.js.map +1 -1
  309. package/dist/core/asyncapi/merge/markers.js +30 -50
  310. package/dist/core/asyncapi/merge/markers.js.map +1 -1
  311. package/dist/core/asyncapi/merge/merge.js +31 -53
  312. package/dist/core/asyncapi/merge/merge.js.map +1 -1
  313. package/dist/core/asyncapi/merge/pin.js +10 -36
  314. package/dist/core/asyncapi/merge/pin.js.map +1 -1
  315. package/dist/core/asyncapi/model.js.map +1 -1
  316. package/dist/core/asyncapi/read.js +36 -12
  317. package/dist/core/asyncapi/read.js.map +1 -1
  318. package/dist/core/brief/brief.js +30 -25
  319. package/dist/core/brief/brief.js.map +1 -1
  320. package/dist/core/brief/landscape.js +8 -12
  321. package/dist/core/brief/landscape.js.map +1 -1
  322. package/dist/core/brief/map/extend.js +8 -5
  323. package/dist/core/brief/map/extend.js.map +1 -1
  324. package/dist/core/brief/map/owed.js +8 -26
  325. package/dist/core/brief/map/owed.js.map +1 -1
  326. package/dist/core/brief/targets.js +79 -5
  327. package/dist/core/brief/targets.js.map +1 -1
  328. package/dist/core/brief/unchecked.js +1 -1
  329. package/dist/core/brief/unchecked.js.map +1 -1
  330. package/dist/core/c4/likec4.js +147 -16
  331. package/dist/core/c4/likec4.js.map +1 -1
  332. package/dist/core/c4/locate/unlocated.js +150 -0
  333. package/dist/core/c4/locate/unlocated.js.map +1 -0
  334. package/dist/core/c4/parsed/values.js +54 -1
  335. package/dist/core/c4/parsed/values.js.map +1 -1
  336. package/dist/core/c4/prefetch/fleet.js +1 -9
  337. package/dist/core/c4/prefetch/fleet.js.map +1 -1
  338. package/dist/core/c4/project/architecture.js +31 -9
  339. package/dist/core/c4/project/architecture.js.map +1 -1
  340. package/dist/core/c4/project/documents.js +36 -10
  341. package/dist/core/c4/project/documents.js.map +1 -1
  342. package/dist/core/c4/project/load.js +14 -21
  343. package/dist/core/c4/project/load.js.map +1 -1
  344. package/dist/core/c4/project/staged.js +7 -3
  345. package/dist/core/c4/project/staged.js.map +1 -1
  346. package/dist/core/c4/resolve/external.js +51 -0
  347. package/dist/core/c4/resolve/external.js.map +1 -0
  348. package/dist/core/c4/resolve/service.js +25 -1
  349. package/dist/core/c4/resolve/service.js.map +1 -1
  350. package/dist/core/c4/seed/template.js +15 -1
  351. package/dist/core/c4/seed/template.js.map +1 -1
  352. package/dist/core/c4/service-model/shape.js +2 -22
  353. package/dist/core/c4/service-model/shape.js.map +1 -1
  354. package/dist/core/c4/source-mask.js +288 -4
  355. package/dist/core/c4/source-mask.js.map +1 -1
  356. package/dist/core/c4/source-scan.js +203 -147
  357. package/dist/core/c4/source-scan.js.map +1 -1
  358. package/dist/core/c4/splice/authored-source.js +19 -0
  359. package/dist/core/c4/splice/authored-source.js.map +1 -1
  360. package/dist/core/c4/splice/delta-blocks.js +5 -18
  361. package/dist/core/c4/splice/delta-blocks.js.map +1 -1
  362. package/dist/core/c4/splice/landscape-merge.js +13 -8
  363. package/dist/core/c4/splice/landscape-merge.js.map +1 -1
  364. package/dist/core/c4/splice/model/merge.js +10 -6
  365. package/dist/core/c4/splice/model/merge.js.map +1 -1
  366. package/dist/core/c4/splice/placement.js +108 -18
  367. package/dist/core/c4/splice/placement.js.map +1 -1
  368. package/dist/core/c4/workspace.js +13 -22
  369. package/dist/core/c4/workspace.js.map +1 -1
  370. package/dist/core/capabilities/capabilities.js +6 -6
  371. package/dist/core/capabilities/capabilities.js.map +1 -1
  372. package/dist/core/capabilities/delta/doc.js +7 -10
  373. package/dist/core/capabilities/delta/doc.js.map +1 -1
  374. package/dist/core/capabilities/findings.js +15 -20
  375. package/dist/core/capabilities/findings.js.map +1 -1
  376. package/dist/core/capabilities/realizes/findings.js +12 -19
  377. package/dist/core/capabilities/realizes/findings.js.map +1 -1
  378. package/dist/core/capabilities/realizes/join.js +0 -20
  379. package/dist/core/capabilities/realizes/join.js.map +1 -1
  380. package/dist/core/capabilities/rollup.js +6 -0
  381. package/dist/core/capabilities/rollup.js.map +1 -1
  382. package/dist/core/capabilities/tree.js +6 -28
  383. package/dist/core/capabilities/tree.js.map +1 -1
  384. package/dist/core/capabilities/usecase-join.js +3 -13
  385. package/dist/core/capabilities/usecase-join.js.map +1 -1
  386. package/dist/core/coherence/authoring/scaffold.js +11 -3
  387. package/dist/core/coherence/authoring/scaffold.js.map +1 -1
  388. package/dist/core/coherence/authoring/sentinels.js +2 -1
  389. package/dist/core/coherence/authoring/sentinels.js.map +1 -1
  390. package/dist/core/coherence/coherence.js +30 -16
  391. package/dist/core/coherence/coherence.js.map +1 -1
  392. package/dist/core/coherence/consumed/operations.js +70 -0
  393. package/dist/core/coherence/consumed/operations.js.map +1 -0
  394. package/dist/core/coherence/consumed/refusal.js +38 -0
  395. package/dist/core/coherence/consumed/refusal.js.map +1 -0
  396. package/dist/core/coherence/declared.js +60 -34
  397. package/dist/core/coherence/declared.js.map +1 -1
  398. package/dist/core/coherence/events/declared.js +44 -59
  399. package/dist/core/coherence/events/declared.js.map +1 -1
  400. package/dist/core/coherence/events/events.js +119 -8
  401. package/dist/core/coherence/events/events.js.map +1 -1
  402. package/dist/core/coherence/events/grades.js +31 -8
  403. package/dist/core/coherence/events/grades.js.map +1 -1
  404. package/dist/core/coherence/events/lookups.js +14 -82
  405. package/dist/core/coherence/events/lookups.js.map +1 -1
  406. package/dist/core/coherence/lookups.js +18 -78
  407. package/dist/core/coherence/lookups.js.map +1 -1
  408. package/dist/core/coherence/retired/archived.js +285 -0
  409. package/dist/core/coherence/retired/archived.js.map +1 -0
  410. package/dist/core/coherence/retired/event-pair.js +62 -0
  411. package/dist/core/coherence/retired/event-pair.js.map +1 -0
  412. package/dist/core/coherence/retired/merged.js +79 -0
  413. package/dist/core/coherence/retired/merged.js.map +1 -0
  414. package/dist/core/coherence/retired/retirement.js +49 -0
  415. package/dist/core/coherence/retired/retirement.js.map +1 -0
  416. package/dist/core/coherence/retired/revival/arrivals.js +131 -0
  417. package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
  418. package/dist/core/coherence/retired/revival/revived.js +297 -0
  419. package/dist/core/coherence/retired/revival/revived.js.map +1 -0
  420. package/dist/core/consumers/after.js +113 -0
  421. package/dist/core/consumers/after.js.map +1 -0
  422. package/dist/core/consumers/consumers.js +296 -0
  423. package/dist/core/consumers/consumers.js.map +1 -0
  424. package/dist/core/consumers/current.js +211 -0
  425. package/dist/core/consumers/current.js.map +1 -0
  426. package/dist/core/consumers/scan.js +46 -0
  427. package/dist/core/consumers/scan.js.map +1 -0
  428. package/dist/core/consumers/state.js +27 -0
  429. package/dist/core/consumers/state.js.map +1 -0
  430. package/dist/core/delta/delta.js +1 -1
  431. package/dist/core/delta/delta.js.map +1 -1
  432. package/dist/core/deployment/delta.js +15 -16
  433. package/dist/core/deployment/delta.js.map +1 -1
  434. package/dist/core/diff/base-git.js +94 -12
  435. package/dist/core/diff/base-git.js.map +1 -1
  436. package/dist/core/diff/base-state.js +28 -16
  437. package/dist/core/diff/base-state.js.map +1 -1
  438. package/dist/core/diff/joins.js +136 -0
  439. package/dist/core/diff/joins.js.map +1 -0
  440. package/dist/core/diff/scope/changed-paths.js +22 -6
  441. package/dist/core/diff/scope/changed-paths.js.map +1 -1
  442. package/dist/core/diff/scope/edges.js +82 -0
  443. package/dist/core/diff/scope/edges.js.map +1 -0
  444. package/dist/core/diff/semantic.js +100 -31
  445. package/dist/core/diff/semantic.js.map +1 -1
  446. package/dist/core/doctor/agents.js +34 -18
  447. package/dist/core/doctor/agents.js.map +1 -1
  448. package/dist/core/doctor/report.js.map +1 -1
  449. package/dist/core/document/apply.js +10 -2
  450. package/dist/core/document/apply.js.map +1 -1
  451. package/dist/core/document/frontmatter.js +55 -0
  452. package/dist/core/document/frontmatter.js.map +1 -1
  453. package/dist/core/document/parse.js +29 -6
  454. package/dist/core/document/parse.js.map +1 -1
  455. package/dist/core/document/scenarios.js +17 -0
  456. package/dist/core/document/scenarios.js.map +1 -1
  457. package/dist/core/document/spec.js +4 -11
  458. package/dist/core/document/spec.js.map +1 -1
  459. package/dist/core/envelope/config.js +10 -0
  460. package/dist/core/envelope/config.js.map +1 -1
  461. package/dist/core/envelope/json.js +34 -0
  462. package/dist/core/envelope/json.js.map +1 -1
  463. package/dist/core/explain/families.js +17 -12
  464. package/dist/core/explain/families.js.map +1 -1
  465. package/dist/core/explain/refusals.js +3 -3
  466. package/dist/core/explain/refusals.js.map +1 -1
  467. package/dist/core/explain/terms.js +18 -9
  468. package/dist/core/explain/terms.js.map +1 -1
  469. package/dist/core/explore/explore.js +2 -7
  470. package/dist/core/explore/explore.js.map +1 -1
  471. package/dist/core/fleet-context.js +34 -95
  472. package/dist/core/fleet-context.js.map +1 -1
  473. package/dist/core/gate/checks.js +10 -3
  474. package/dist/core/gate/checks.js.map +1 -1
  475. package/dist/core/gate/partners.js +64 -19
  476. package/dist/core/gate/partners.js.map +1 -1
  477. package/dist/core/gate/report.js.map +1 -1
  478. package/dist/core/gate/verification.js +75 -12
  479. package/dist/core/gate/verification.js.map +1 -1
  480. package/dist/core/gherkin/stale.js +13 -37
  481. package/dist/core/gherkin/stale.js.map +1 -1
  482. package/dist/core/glossary/tree.js +6 -19
  483. package/dist/core/glossary/tree.js.map +1 -1
  484. package/dist/core/kernel/concurrency.js +19 -0
  485. package/dist/core/kernel/concurrency.js.map +1 -1
  486. package/dist/core/kernel/records.js +11 -0
  487. package/dist/core/kernel/records.js.map +1 -1
  488. package/dist/core/links/backlinks.js +1 -3
  489. package/dist/core/links/backlinks.js.map +1 -1
  490. package/dist/core/links/corpus.js +5 -36
  491. package/dist/core/links/corpus.js.map +1 -1
  492. package/dist/core/mcp/argv.js +101 -0
  493. package/dist/core/mcp/argv.js.map +1 -1
  494. package/dist/core/mcp/author-tools.js +2 -1
  495. package/dist/core/mcp/author-tools.js.map +1 -1
  496. package/dist/core/mcp/protocol.js +2 -2
  497. package/dist/core/mcp/protocol.js.map +1 -1
  498. package/dist/core/mcp/tools.js +43 -33
  499. package/dist/core/mcp/tools.js.map +1 -1
  500. package/dist/core/openapi/baseline/gate.js +53 -12
  501. package/dist/core/openapi/baseline/gate.js.map +1 -1
  502. package/dist/core/openapi/baseline/plan.js +22 -8
  503. package/dist/core/openapi/baseline/plan.js.map +1 -1
  504. package/dist/core/openapi/baseline/record.js +0 -1
  505. package/dist/core/openapi/baseline/record.js.map +1 -1
  506. package/dist/core/openapi/contract-text.js +33 -0
  507. package/dist/core/openapi/contract-text.js.map +1 -0
  508. package/dist/core/openapi/contract-tree.js +56 -0
  509. package/dist/core/openapi/contract-tree.js.map +1 -0
  510. package/dist/core/openapi/depth.js +38 -24
  511. package/dist/core/openapi/depth.js.map +1 -1
  512. package/dist/core/openapi/digest.js +52 -2
  513. package/dist/core/openapi/digest.js.map +1 -1
  514. package/dist/core/openapi/doc.js +12 -4
  515. package/dist/core/openapi/doc.js.map +1 -1
  516. package/dist/core/openapi/merge/components.js +25 -60
  517. package/dist/core/openapi/merge/components.js.map +1 -1
  518. package/dist/core/openapi/merge/error.js +3 -13
  519. package/dist/core/openapi/merge/error.js.map +1 -1
  520. package/dist/core/openapi/merge/markers.js +26 -30
  521. package/dist/core/openapi/merge/markers.js.map +1 -1
  522. package/dist/core/openapi/merge/merge.js +26 -52
  523. package/dist/core/openapi/merge/merge.js.map +1 -1
  524. package/dist/core/openapi/merge/pin.js +15 -35
  525. package/dist/core/openapi/merge/pin.js.map +1 -1
  526. package/dist/core/openspec/decide/verdict.js.map +1 -1
  527. package/dist/core/openspec/inventory.js +4 -5
  528. package/dist/core/openspec/inventory.js.map +1 -1
  529. package/dist/core/openspec/model/mapping.js +30 -28
  530. package/dist/core/openspec/model/mapping.js.map +1 -1
  531. package/dist/core/openspec/model/model.js +12 -7
  532. package/dist/core/openspec/model/model.js.map +1 -1
  533. package/dist/core/openspec/scan/changes.js +10 -4
  534. package/dist/core/openspec/scan/changes.js.map +1 -1
  535. package/dist/core/openspec/scan/config.js +7 -16
  536. package/dist/core/openspec/scan/config.js.map +1 -1
  537. package/dist/core/openspec/scan/shape.js +63 -19
  538. package/dist/core/openspec/scan/shape.js.map +1 -1
  539. package/dist/core/openspec/scan/walk.js +21 -38
  540. package/dist/core/openspec/scan/walk.js.map +1 -1
  541. package/dist/core/pack/living.js +2 -16
  542. package/dist/core/pack/living.js.map +1 -1
  543. package/dist/core/provenance/findings.js +48 -12
  544. package/dist/core/provenance/findings.js.map +1 -1
  545. package/dist/core/provenance/git.js +33 -57
  546. package/dist/core/provenance/git.js.map +1 -1
  547. package/dist/core/provenance/gitq/moves.js +71 -114
  548. package/dist/core/provenance/gitq/moves.js.map +1 -1
  549. package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
  550. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
  551. package/dist/core/provenance/sources.js +23 -6
  552. package/dist/core/provenance/sources.js.map +1 -1
  553. package/dist/core/provenance/stamp.js +5 -17
  554. package/dist/core/provenance/stamp.js.map +1 -1
  555. package/dist/core/repo/entries.js.map +1 -1
  556. package/dist/core/repo/paths.js +34 -29
  557. package/dist/core/repo/paths.js.map +1 -1
  558. package/dist/core/repo/repo.js +24 -14
  559. package/dist/core/repo/repo.js.map +1 -1
  560. package/dist/core/repo/tree/find.js +12 -3
  561. package/dist/core/repo/tree/find.js.map +1 -1
  562. package/dist/core/repo/tree/fs.js +27 -1
  563. package/dist/core/repo/tree/fs.js.map +1 -1
  564. package/dist/core/repo/tree/render/body.js +45 -6
  565. package/dist/core/repo/tree/render/body.js.map +1 -1
  566. package/dist/core/repo/tree/render/service/header.js +125 -0
  567. package/dist/core/repo/tree/render/service/header.js.map +1 -0
  568. package/dist/core/repo/tree/render/service/peers.js +28 -0
  569. package/dist/core/repo/tree/render/service/peers.js.map +1 -0
  570. package/dist/core/repo/tree/render/service/view-id.js +67 -0
  571. package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
  572. package/dist/core/repo/tree/render/service/views.js +78 -0
  573. package/dist/core/repo/tree/render/service/views.js.map +1 -0
  574. package/dist/core/repo/tree/render/stale.js +19 -2
  575. package/dist/core/repo/tree/render/stale.js.map +1 -1
  576. package/dist/core/repo/tree/render/views.js +102 -33
  577. package/dist/core/repo/tree/render/views.js.map +1 -1
  578. package/dist/core/repo/tree/walk.js +8 -30
  579. package/dist/core/repo/tree/walk.js.map +1 -1
  580. package/dist/core/results.js +47 -12
  581. package/dist/core/results.js.map +1 -1
  582. package/dist/core/staging/commit.js +10 -12
  583. package/dist/core/staging/commit.js.map +1 -1
  584. package/dist/core/staging/lock.js +74 -12
  585. package/dist/core/staging/lock.js.map +1 -1
  586. package/dist/core/staging/recovery/intent.js +2 -5
  587. package/dist/core/staging/recovery/intent.js.map +1 -1
  588. package/dist/core/staging/recovery/recover.js +60 -1
  589. package/dist/core/staging/recovery/recover.js.map +1 -1
  590. package/dist/core/staging/snapshot/manifest.js +152 -0
  591. package/dist/core/staging/snapshot/manifest.js.map +1 -0
  592. package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +10 -34
  593. package/dist/core/staging/snapshot/snapshot.js.map +1 -0
  594. package/dist/core/status/actions/execution.js +11 -0
  595. package/dist/core/status/actions/execution.js.map +1 -1
  596. package/dist/core/status/feature/artifacts.js +2 -2
  597. package/dist/core/status/feature/artifacts.js.map +1 -1
  598. package/dist/core/status/feature/feature.js +5 -11
  599. package/dist/core/status/feature/feature.js.map +1 -1
  600. package/dist/core/status/fleet/fleet.js +6 -2
  601. package/dist/core/status/fleet/fleet.js.map +1 -1
  602. package/dist/core/status/fleet/next.js +26 -3
  603. package/dist/core/status/fleet/next.js.map +1 -1
  604. package/dist/core/status/report.js.map +1 -1
  605. package/dist/core/status/verification.js +30 -15
  606. package/dist/core/status/verification.js.map +1 -1
  607. package/dist/core/tasks/packet.js +174 -0
  608. package/dist/core/tasks/packet.js.map +1 -0
  609. package/dist/core/tasks/record.js +43 -0
  610. package/dist/core/tasks/record.js.map +1 -0
  611. package/dist/core/tasks/sources.js +181 -0
  612. package/dist/core/tasks/sources.js.map +1 -0
  613. package/dist/core/tasks/stamps.js +76 -0
  614. package/dist/core/tasks/stamps.js.map +1 -0
  615. package/dist/core/tasks/unit.js +0 -0
  616. package/dist/core/tasks/unit.js.map +1 -0
  617. package/dist/core/usecases/capability.js +11 -14
  618. package/dist/core/usecases/capability.js.map +1 -1
  619. package/dist/core/usecases/delta/flows.js +5 -7
  620. package/dist/core/usecases/delta/flows.js.map +1 -1
  621. package/dist/core/usecases/fleet.js +178 -31
  622. package/dist/core/usecases/fleet.js.map +1 -1
  623. package/dist/core/usecases/operations.js +5 -4
  624. package/dist/core/usecases/operations.js.map +1 -1
  625. package/dist/core/usecases/service/flows.js +9 -6
  626. package/dist/core/usecases/service/flows.js.map +1 -1
  627. package/dist/core/usecases/suites/participants.js +83 -0
  628. package/dist/core/usecases/suites/participants.js.map +1 -0
  629. package/dist/core/usecases/suites/suites.js +45 -0
  630. package/dist/core/usecases/suites/suites.js.map +1 -0
  631. package/dist/core/verify/answers.js +12 -5
  632. package/dist/core/verify/answers.js.map +1 -1
  633. package/dist/core/verify/build.js +102 -82
  634. package/dist/core/verify/build.js.map +1 -1
  635. package/dist/core/verify/checklist.js +45 -10
  636. package/dist/core/verify/checklist.js.map +1 -1
  637. package/dist/core/verify/location.js +67 -0
  638. package/dist/core/verify/location.js.map +1 -0
  639. package/dist/core/verify/pins/lint.js +3 -2
  640. package/dist/core/verify/pins/lint.js.map +1 -1
  641. package/dist/core/verify/pins/pin.js +1 -1
  642. package/dist/core/verify/pins/pin.js.map +1 -1
  643. package/dist/core/verify/read/file.js +224 -0
  644. package/dist/core/verify/read/file.js.map +1 -0
  645. package/dist/core/verify/{file.js → read/shape.js} +16 -104
  646. package/dist/core/verify/read/shape.js.map +1 -0
  647. package/dist/core/verify/read/union.js +105 -0
  648. package/dist/core/verify/read/union.js.map +1 -0
  649. package/dist/core/verify/record.js +35 -12
  650. package/dist/core/verify/record.js.map +1 -1
  651. package/dist/core/verify/store/commit.js +40 -30
  652. package/dist/core/verify/store/commit.js.map +1 -1
  653. package/dist/core/verify/store/render.js +8 -4
  654. package/dist/core/verify/store/render.js.map +1 -1
  655. package/dist/core/vocabulary/issue.js +1 -0
  656. package/dist/core/vocabulary/issue.js.map +1 -1
  657. package/dist/core/vocabulary/maturity.js +53 -3
  658. package/dist/core/vocabulary/maturity.js.map +1 -1
  659. package/examples/README.md +28 -9
  660. package/examples/docs/architecture/landscape.likec4 +21 -7
  661. package/examples/docs/architecture/services.likec4 +70 -0
  662. package/examples/docs/architecture/subsystems.likec4 +3 -0
  663. package/examples/docs/architecture/usecases/order-notification.likec4 +6 -2
  664. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
  665. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
  666. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
  667. package/examples/docs/services/order-service/asyncapi.yaml +3 -2
  668. package/examples/docs/services/order-service/model.likec4 +10 -8
  669. package/examples/docs/services/payment-service/model.likec4 +6 -4
  670. package/examples/docs/services/platform/identity-service/model.likec4 +9 -7
  671. package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
  672. package/examples/docs/services/platform/notification-service/model.likec4 +4 -5
  673. package/package.json +1 -1
  674. package/dist/commands/validate/checks/vocabulary.js +0 -16
  675. package/dist/commands/validate/checks/vocabulary.js.map +0 -1
  676. package/dist/core/agent/agents-md/artifacts.js +0 -221
  677. package/dist/core/agent/agents-md/artifacts.js.map +0 -1
  678. package/dist/core/agent/agents-md/cycle.js +0 -180
  679. package/dist/core/agent/agents-md/cycle.js.map +0 -1
  680. package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
  681. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
  682. package/dist/core/agent/agents-md/spine.js +0 -40
  683. package/dist/core/agent/agents-md/spine.js.map +0 -1
  684. package/dist/core/coherence/events/merged.js +0 -40
  685. package/dist/core/coherence/events/merged.js.map +0 -1
  686. package/dist/core/diff/current-state.js +0 -96
  687. package/dist/core/diff/current-state.js.map +0 -1
  688. package/dist/core/diff/victims.js +0 -256
  689. package/dist/core/diff/victims.js.map +0 -1
  690. package/dist/core/staging/snapshot.js.map +0 -1
  691. package/dist/core/verify/file.js.map +0 -1
@@ -1,9 +1,13 @@
1
1
  /**
2
2
  * What loam REFUSES: the OpenSpec migration surface, the containment and
3
3
  * unreadable-file findings, and the error-envelope codes. What happens to a
4
- * SHIPPED change — the archive gate, unarchive, and dropping a feature — is
5
- * the continuation section in ./shipped/archive-gate.ts, split there because
6
- * the two subjects had grown past one file's ceiling.
4
+ * SHIPPED change — the archive gate, unarchive, and dropping a feature — used
5
+ * to be a continuation section beside this one, split off when the two
6
+ * subjects had grown past one file's ceiling. Nothing assembled it into
7
+ * anything loam shipped once AGENTS.md became ../agents-md.ts's ORIENTATION,
8
+ * and it was deleted: that teaching is the `loam-ship` protocol
9
+ * (../workflows/closing.ts) and ORIENTATION's own "Reading loam's output" and
10
+ * "Safety and hand-back" sections.
7
11
  *
8
12
  * Codes here are LISTED by the command that raises them, not glossed; the
9
13
  * meanings come from `loam explain <code>`, out of the running binary. See
@@ -20,11 +24,11 @@
20
24
  * resolve, so a family can never quietly lose its gloss and its explanation
21
25
  * at once.
22
26
  *
23
- * One section of the AGENTS.md template. ../agents-md.ts assembles the
24
- * document by PLAIN CONCATENATION — no join separator — so every section
25
- * starts at the first character of its opening line and ends with the newline
26
- * that closes its last one. Keep that shape when editing, or two sections glue
27
- * onto one line in every docs repo loam scaffolds from now on.
27
+ * The last part of the `loam-codes` page, not a section of AGENTS.md any more:
28
+ * ../workflows/reference/codes.ts assembles that page by PLAIN CONCATENATION —
29
+ * no join separator — so this string starts at the first character of its
30
+ * opening line and ends with the newline that closes its last one. Keep that
31
+ * shape when editing, or two parts glue onto one line on the printed page.
28
32
  */
29
33
  export const REFUSALS = `- \`loam audit-openspec <root>\` is the read-only OpenSpec inventory; an explicit
30
34
  \`--write-mapping\` writes a non-overwriting, digest-bound decision skeleton
@@ -90,7 +94,9 @@ export const REFUSALS = `- \`loam audit-openspec <root>\` is the read-only OpenS
90
94
  \`services-missing\`.
91
95
  - Both modes read frontmatter (\`frontmatter.missing\`, \`frontmatter.malformed\`,
92
96
  \`frontmatter.field-mismatch\`,
93
- \`frontmatter.status-unknown\`, \`frontmatter.field-missing\`); a service's spec.md —
97
+ \`frontmatter.status-unknown\`, \`frontmatter.field-missing\`, and
98
+ \`frontmatter.owner-mismatch\` when one service's spec.md and arch.spec.md name two
99
+ different owners); a service's spec.md —
94
100
  and its arch.spec.md, same conventions — additionally carries the sources chain
95
101
  (\`sources.absent\`, \`sources.path-missing\`, \`sources.empty\`, \`sources.skipped\`,
96
102
  \`sources.unvouched\`, \`sources.stale\`) plus the two doc-side checks,
@@ -103,8 +109,11 @@ export const REFUSALS = `- \`loam audit-openspec <root>\` is the read-only OpenS
103
109
  \`evidence.token-missing\` and \`evidence.record-unreadable\` the warnings.
104
110
  Demote-only, by doctrine: a warn here is a reading priority for a reviewer,
105
111
  never a verdict change — no pin state moves \`attested\` or \`verified\` in
106
- either direction. From the docs repo the family is silent;
107
- \`sources.unverifiable-from-here\` already names that blind spot.
112
+ either direction. From the docs repo the service target's family is silent;
113
+ \`sources.unverifiable-from-here\` already names that blind spot. The record
114
+ itself lives in the docs repo, so the FEATURE target (\`--feature\`, and every
115
+ active feature under \`--all\`) raises \`evidence.record-unreadable\` from
116
+ anywhere — the same warn, about readability only.
108
117
  - \`loam archive\` alone reports the breaches only the merge computation can see:
109
118
  \`living.requirement-outside-requirements\`, \`openapi.op-modified\`,
110
119
  \`openapi.path-item-modified\`, \`openapi.component-modified\`,
@@ -159,8 +168,9 @@ filtered-and-ordered row order, \`unowned[]\` listing the rows no rule matched,
159
168
  worklists. A CODEOWNERS path that cannot be read, or a line that cannot be
160
169
  parsed as \`pattern owner…\`, refuses \`owners-unreadable\` naming the path and
161
170
  line — fail-closed, exactly as \`answers-unreadable\` treats the other
162
- user-named file. Either flag with the \`features\` or \`capabilities\` section
163
- is refused (\`invalid-option\`).
171
+ user-named file. Either flag with the \`features\`, \`capabilities\`, \`glossary\`
172
+ or \`suites\` section is refused (\`invalid-option\`), as is \`--needs-work\` with
173
+ \`features\` or \`suites\`, and a \`[name]\` after any section but \`suites\`.
164
174
 
165
175
  \`--strict\` (every targeting mode, \`--all\` included) exits 1 when any error
166
176
  or warning exists — \`ok\`-severity findings are confirmations and never trip
@@ -221,15 +231,20 @@ follows is the other half — which command raises which:
221
231
  - \`loam verify --record\` / \`--results\` / \`--contract-results\`:
222
232
  \`answers-unreadable\`, \`answers-mismatch\`, \`answers-unevidenced\`,
223
233
  \`record-federated\`, \`record-unreadable\` and \`record-raced\`. Two records for
224
- different services land in either order — the later one waits, then merges,
225
- and both attestations survive.
234
+ different services land in either order — each writes only its own
235
+ \`verification/<svc>.yaml\`, so both attestations survive, on one checkout or
236
+ on two docs-repo branches merged later. A \`record-unreadable\` names the file
237
+ that failed and its safe repair; never delete the one-file
238
+ \`verification.yaml\`, which may hold other services' attestations.
226
239
  - \`loam open\`: \`no-members\`, \`binding-duplicate\`, \`already-exists\` (the
227
240
  workspace file, which loam never silently overwrites — pass \`--out\` or
228
241
  \`--force\`) and \`invalid-option\`.
229
242
  - \`loam seed --from fleet.yaml\`: \`seed-file-invalid\`, \`seed-duplicate-service\`,
230
- \`seed-unknown-subsystem\`, \`seed-landscape-edited\`, and \`unknown-service\` for a
231
- call endpoint nothing declares. An existing service directory is never moved,
232
- and nothing is ever deleted.
243
+ \`seed-unknown-subsystem\`, \`seed-landscape-edited\`, \`already-exists\` when a
244
+ generated views file in \`architecture/\` was written by somebody rather than by
245
+ loam (no \`// generated by loam\` first line — seed writes nothing at all in that
246
+ case), and \`unknown-service\` for a call endpoint nothing declares. An existing
247
+ service directory is never moved, and nothing is ever deleted.
233
248
  - \`internal\` — an unexpected throw, the one code with no stable meaning.
234
249
 
235
250
  \`--all\` reports a target per service, a target per feature in flight, and one target
@@ -240,7 +255,8 @@ Three different words for three different questions, and no command conflates th
240
255
  somebody says the code was built and showed evidence (\`verify\`). A feature can be
241
256
  valid and unverified, or verified and incoherent. Read the one you meant.
242
257
  `;
243
- /* The archive gate, unarchive, and dropping a feature continue in
244
- * ./shipped/archive-gate.ts — one document section split at its own subject
245
- * seam, concatenated after this one by ../agents-md.ts. */
258
+ /* The archive gate, unarchive, and dropping a feature are not continued here:
259
+ * the section that once followed this one was never assembled again after
260
+ * AGENTS.md became ORIENTATION, and was deleted — see this file's header for
261
+ * where that teaching ships now. */
246
262
  //# sourceMappingURL=refusals.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"refusals.js","sourceRoot":"","sources":["../../../../src/core/agent/agents-md/refusals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqNvB,CAAC;AAEF;;2DAE2D","sourcesContent":["/**\n * What loam REFUSES: the OpenSpec migration surface, the containment and\n * unreadable-file findings, and the error-envelope codes. What happens to a\n * SHIPPED change — the archive gate, unarchive, and dropping a feature — is\n * the continuation section in ./shipped/archive-gate.ts, split there because\n * the two subjects had grown past one file's ceiling.\n *\n * Codes here are LISTED by the command that raises them, not glossed; the\n * meanings come from `loam explain <code>`, out of the running binary. See\n * ./command-map.ts's header for why the duplicated gloss left.\n *\n * THE ONE EXCEPTION IS THE FIRST BULLET. `loam explain` cannot answer\n * `openspec.*` or `mapping.*` — those commands grade a repository still in\n * another tool's shape, before it has a governed loop to look a code up from,\n * and the catalogue leaves them unanswered for that reason. Pointing this\n * section's reader at an explanation that does not exist is the exact failure\n * the pointer exists to prevent, so the migration notes stay written out here\n * until the catalogue reaches them. test/agent-contract.test.ts enforces the\n * boundary from the other side: every OTHER code backticked in AGENTS_MD must\n * resolve, so a family can never quietly lose its gloss and its explanation\n * at once.\n *\n * One section of the AGENTS.md template. ../agents-md.ts assembles the\n * document by PLAIN CONCATENATION — no join separator — so every section\n * starts at the first character of its opening line and ends with the newline\n * that closes its last one. Keep that shape when editing, or two sections glue\n * onto one line in every docs repo loam scaffolds from now on.\n */\nexport const REFUSALS = `- \\`loam audit-openspec <root>\\` is the read-only OpenSpec inventory; an explicit\n \\`--write-mapping\\` writes a non-overwriting, digest-bound decision skeleton\n outside the source tree. \\`loam migrate-openspec <root> --map <file>\\` validates\n that mapping and stays dry-run unless both \\`--apply\\` and an empty \\`--target\\`\n are supplied. Living/active shape diagnostics are \\`openspec.workspace-empty\\`\n (the audited root holds no living spec and no active change, so there is\n nothing to migrate and no verdict over it can be \\`ready\\`),\n \\`openspec.specs-missing\\`,\n \\`openspec.config-invalid\\`, \\`openspec.external-store-pointer\\`,\n \\`openspec.living-empty\\`, \\`openspec.living-requirements-outside-section\\`,\n \\`openspec.living-delta-section\\`, \\`openspec.nonstandard-living-spec\\` (markdown\n under \\`specs/\\` named neither spec.md nor design.md, so no capability reads\n it), \\`openspec.change-metadata-invalid\\`,\n \\`openspec.change-schema-unresolved\\`, \\`openspec.skip-specs-with-specs\\`,\n \\`openspec.change-no-specs\\`, \\`openspec.change-empty\\`,\n \\`openspec.change-without-delta-sections\\`,\n \\`openspec.change-requirements-outside-delta-sections\\`,\n \\`openspec.change-quoted-requirements\\` (requirements under a \\`## Requirements\\`\n heading INSIDE a change delta — the shape OpenSpec's own living-spec template\n mandates, and one that stages nothing, so loam refuses it rather than counting\n requirements it cannot route),\n \\`openspec.hidden-change-directory\\` (a dot-prefixed directory under\n \\`changes/\\` is not enumerated as a change, so nothing under it migrates —\n rename it or move it out; under \\`changes/archive/\\` the same code is an archive\n diagnostic and does not gate, because frozen history never blocks),\n \\`openspec.nonstandard-change-spec\\`, \\`openspec.renamed-malformed\\`,\n \\`openspec.requirement-id-invalid\\`, \\`openspec.requirement-id-repeated\\`,\n \\`openspec.requirement-id-duplicate\\` (a stable \\`Requirement-ID\\` that is\n malformed, declared twice, or shared by two requirements — identity is never\n inferred from an invalid declaration, so the RENAMED chain cannot be followed\n through it), \\`openspec.symlink-unsupported\\`, and \\`openspec.non-utf8-artifact\\`;\n the same shapes in frozen history are non-blocking archive diagnostics.\n Mapping refuses stale or incomplete decisions under \\`mapping.source-missing\\`,\n \\`mapping.source-root-mismatch\\`, \\`mapping.source-digest-mismatch\\`,\n \\`mapping.unknown-capability\\`, \\`mapping.unknown-requirement\\`,\n \\`mapping.requirement-allocation-missing\\`,\n \\`mapping.requirement-service-unknown\\`, \\`mapping.service-allocation-empty\\`,\n \\`mapping.unknown-change\\`, \\`mapping.change-title-missing\\`,\n \\`mapping.feature-id-invalid\\`, \\`mapping.feature-id-duplicate\\`,\n \\`mapping.unknown-rename\\`, \\`mapping.invalid-requirement-id\\`,\n \\`mapping.rename-source-missing\\`, \\`mapping.rename-source-ambiguous\\`,\n \\`mapping.rename-source-id-invalid\\`, \\`mapping.rename-target-conflict\\`,\n \\`mapping.rename-existing-id-conflict\\`, \\`mapping.rename-id-conflict\\`,\n \\`mapping.rename-double-source\\`, \\`mapping.rename-double-target\\`,\n \\`mapping.rename-chain\\`,\n \\`mapping.unknown-artifact\\`, and \\`mapping.invalid-artifact-disposition\\`.\n- Repository containment failures are explicit: \\`sources.path-outside\\` rejects a\n provenance source outside the service repo and \\`gherkin.path-outside\\` rejects a\n generated-suite target outside it. Federated verify recording refuses under\n \\`service-mismatch\\`, \\`unknown-service\\` or \\`repository-unavailable\\` when the\n selected service, fleet identity and repository commit cannot be bound safely,\n and under \\`record-federated\\` when the legacy all-at-once form would erase other\n repositories' attestations. \\`loam gherkin --service <id>\\` and\n \\`loam vouch --service <id>\\` answer that same wrong-repo pair\n (\\`repository-unavailable\\`, \\`service-mismatch\\`) for the same reason. Neither is\n \\`invalid-option\\`: the flags were right and the directory was wrong, so the fix\n is to re-run in the service's own checkout, never to re-read the invocation.\n- A file loam cannot read is reported, never skipped: \\`service.unreadable\\` and\n \\`feature.unreadable\\` (both errors) name the path and say that nothing about\n that target was checked while the rest of the fleet still was. The whole docs\n repo being unreachable is a refusal instead: \\`docs-missing\\` or\n \\`services-missing\\`.\n- Both modes read frontmatter (\\`frontmatter.missing\\`, \\`frontmatter.malformed\\`,\n \\`frontmatter.field-mismatch\\`,\n \\`frontmatter.status-unknown\\`, \\`frontmatter.field-missing\\`); a service's spec.md —\n and its arch.spec.md, same conventions — additionally carries the sources chain\n (\\`sources.absent\\`, \\`sources.path-missing\\`, \\`sources.empty\\`, \\`sources.skipped\\`,\n \\`sources.unvouched\\`, \\`sources.stale\\`) plus the two doc-side checks,\n \\`content.stale\\` and \\`sources.sampled-vouch\\`, which need no service repo and so\n fire from the docs repo too.\n- A service target run from the service's own repository also re-checks the\n \\`evidence_pins\\` its federated verification records carry, against the working\n tree: \\`evidence.checked\\` and \\`evidence.unpinned\\` are the confirmations,\n \\`evidence.unresolved\\`, \\`evidence.moved\\`, \\`evidence.line-changed\\`,\n \\`evidence.token-missing\\` and \\`evidence.record-unreadable\\` the warnings.\n Demote-only, by doctrine: a warn here is a reading priority for a reviewer,\n never a verdict change — no pin state moves \\`attested\\` or \\`verified\\` in\n either direction. From the docs repo the family is silent;\n \\`sources.unverifiable-from-here\\` already names that blind spot.\n- \\`loam archive\\` alone reports the breaches only the merge computation can see:\n \\`living.requirement-outside-requirements\\`, \\`openapi.op-modified\\`,\n \\`openapi.path-item-modified\\`, \\`openapi.component-modified\\`,\n \\`openapi.ref-unresolved\\`, \\`openapi.remove-marker-path-level\\`, the event\n axis's own trio \\`asyncapi.message-modified\\` / \\`asyncapi.channel-modified\\` /\n \\`asyncapi.operation-modified\\`, \\`asyncapi.ref-unresolved\\`,\n \\`asyncapi.remove-marker-inline\\` and \\`service.no-model\\`. Two of those are\n graded at validate time as well and are STRICTER here —\n \\`openapi.ref-unresolved\\` and \\`asyncapi.ref-unresolved\\` are warnings there and\n errors here, because the merged document is what would carry the dangling\n reference — so \\`loam explain\\` on either prints both contexts.\n\n\\`loam validate <target>\\` is the positional spelling of the first two: a feature id\nor a service id, tried in that order, so the feature wins when one name could be\nboth and \\`--service\\`/\\`--feature\\` force the reading. When the name really is both,\nthe run says so — \\`target.ambiguous\\` (warn) names the reading it took and the\nflag that forces the other, and \\`--json\\` carries \\`resolvedKind\\` — because a\nsilent precedence rule is a report about a target nobody asked for. The\npositional together with \\`--all\\`, \\`--service\\` or \\`--feature\\` is refused\n(\\`invalid-option\\`).\n\nTwo rendering levers change nothing else: \\`loam validate --errors-only\\` drops\nthe confirmations from the TEXT view (the \\`--json\\` payload is byte-identical),\nand \\`loam list --needs-work\\` narrows the service list to the ones with\nsomething missing — the adoption worklist. \\`loam list --needs-work --review-order\\`\norders that worklist by fan-in — the services the most other services depend on\nfirst: drawn call edges into each, plus event subscriptions (a drawn \\`consumes\\`\nedge, or a living \\`Consumes:\\` line naming a message it sends) — a count, never\na priority judgement — and its \\`--json\\` rows then additionally carry \\`fanIn\\`\nand \\`reviewRank\\`; without \\`--needs-work\\` the flag is refused (\\`invalid-option\\`).\n\nTwo campaign flags slice that same services section and change nothing about\nany row. \\`loam list --subsystem <name>\\` limits the listing to the services\nfiled under one subsystem, at any depth: \\`services[]\\`, the \\`maturity\\` rollup\nand \\`subsystems[]\\` reflect the slice (\\`unfiledServices\\` is omitted — it is a\nfleet-root fact), while every per-row fact — \\`fanIn\\`, \\`apiExpected\\`,\n\\`missing\\` — is still computed fleet-wide, so filtering changes which rows\nappear, never what a row says; with \\`--review-order\\` the filter applies first\nand \\`reviewRank\\` stays contiguous within the filtered worklist. The name\n\\`unfiled\\` selects the services filed under no subsystem while nothing in the\ntree claims that name (a real subsystem or service spelled \\`unfiled\\` wins).\nAn unknown name refuses \\`unknown-target\\` with close-name hints; a service\nname refuses \\`invalid-option\\`. \\`loam list --owners <path>\\` joins each listed\nservice's directory to the owning teams in the named CODEOWNERS file —\ndirectory-pattern rules only, last match wins, an owner-less directory rule\nCLEARING ownership for what it matches exactly as the forge reads it; a\nrecognised rule outside that subset (any other wildcard use, \\`*\\` included)\nis reported under \\`skippedRules\\`, never guessed at — and \\`--json\\` gains the additive \\`owners\\`\nkey (\\`path\\`, \\`teams[]\\` with each team's services in the listing's own\nfiltered-and-ordered row order, \\`unowned[]\\` listing the rows no rule matched,\n\\`skippedRules[]\\`), so the per-team arrays are the per-team campaign\nworklists. A CODEOWNERS path that cannot be read, or a line that cannot be\nparsed as \\`pattern owner…\\`, refuses \\`owners-unreadable\\` naming the path and\nline — fail-closed, exactly as \\`answers-unreadable\\` treats the other\nuser-named file. Either flag with the \\`features\\` or \\`capabilities\\` section\nis refused (\\`invalid-option\\`).\n\n\\`--strict\\` (every targeting mode, \\`--all\\` included) exits 1 when any error\nor warning exists — \\`ok\\`-severity findings are confirmations and never trip\nit. It changes the exit code and nothing else:\n\\`valid\\` still means \"no errors\", and the \\`--json\\` payload stays byte-for-byte\nwhat it was. The stricter grade is a per-invocation lever, visible in the CI\npipeline that passes the flag — deliberately not a per-repo profile.\n\n\\`loam delta <FEAT> --json\\` exits 1 when either authored document behind the brief\nfailed to parse — \\`architecture.errors\\` non-empty, or \\`openapi.unreadable\\` true —\nwith \\`ok: true\\` and the full payload intact: the empty C4 slice means the delta\ndid not parse, and an empty \\`api\\` beside \\`openapi.unreadable\\` means the contract\ndelta did not parse, neither of them \"this feature changes nothing there\". Branch\non the exit code before consuming either slice as a task brief.\n\nFindings with severity \\`ok\\` are confirmations, not work: \\`c4.valid\\`, \\`delta.valid\\`,\n\\`requirements.covered\\`, \\`api.covered\\`, \\`spine.resolved\\`, \\`event.covered\\`, \\`coherence.ok\\`,\n\\`landscape.matched\\`, \\`archedge.covered\\`, \\`sources.resolved\\`, \\`sources.current\\`,\n\\`sources.walked\\`, \\`gherkin.current\\`.\n\nA finding's \\`subject\\` names the service it is about. The envelope separates \\`ok\\` (the\ncommand ran) from \\`valid\\` (the docs pass). A refusal is \\`ok: false\\` with a stable\n\\`error.code\\`, and \\`loam explain <code>\\` has the meaning of every one of them. What\nfollows is the other half — which command raises which:\n\n- Before any command can read anything: \\`no-config\\`, \\`config-invalid\\`,\n \\`docs-missing\\`, \\`services-missing\\`, \\`unknown-target\\`, \\`invalid-option\\`.\n \\`invalid-option\\` covers flags that contradict each other AND a value that\n cannot be right — a \\`loam list\\` section that is not services or features, or a\n service id that is not a legal \\`services/<id>/\\` directory name.\n- Every JOURNALED writer — \\`archive\\`, \\`unarchive\\`, \\`rebase\\`, \\`vouch\\`, \\`new\\`,\n \\`gherkin\\`, \\`seed\\`, \\`subsystem\\` and \\`verify --record\\` — shares one transaction\n and therefore one set of refusals: \\`docs-busy\\`, \\`commit-interrupted\\`,\n \\`merge-failed\\` and \\`rollback-incomplete\\`. Learn them once and you have read\n them for all nine. \\`loam doctor\\` names the interrupted state as\n \\`doctor.commit-interrupted\\` and \\`loam validate\\` leads every mode with\n \\`docs.commit-interrupted\\` while the journal sits there; every journaled writer\n recovers a finished predecessor's journal on its next run and reports it as\n \\`recovered\\` in \\`--json\\`.\n- \\`loam new\\`: \\`already-exists\\` — also the answer when a concurrent \\`new\\` for the\n same id wins the race.\n- \\`loam vouch\\`: \\`sources-absent\\`, \\`sources-path-missing\\`, \\`vouch-raced\\`, and the\n wrong-repo pair \\`repository-unavailable\\` / \\`service-mismatch\\`, because\n \\`sources\\` resolve against the service's own checkout and from anywhere else\n those paths are somebody else's.\n- \\`loam archive\\`: \\`not-coherent\\`, \\`living-outside-requirements\\`,\n \\`archive-exists\\` — see the archive gate below.\n- \\`loam unarchive\\`: \\`feature-active\\`, \\`snapshot-missing\\`, \\`snapshot-stale\\`,\n \\`snapshot-corrupt\\`, \\`restore-failed\\` — see \"Taking an archive back\".\n- \\`loam gherkin\\`: \\`gherkin-conflict\\`, plus the same wrong-repo pair, refused\n before anything is written because the generated files land in the repo loam\n is standing in.\n- \\`loam subsystem\\`: \\`subsystem-not-empty\\`, \\`move-uncommitted\\`, \\`move-failed\\`,\n and \\`unknown-target\\` / \\`already-exists\\` / \\`invalid-option\\` for names that\n resolve to nothing, collide in the flat namespace, or break the grammar.\n \\`move-failed\\` is deliberately distinct from \\`merge-failed\\`: the move's own\n transaction rolled back cleanly, nothing changed, and re-running can work.\n- \\`loam verify --record\\` / \\`--results\\` / \\`--contract-results\\`:\n \\`answers-unreadable\\`, \\`answers-mismatch\\`, \\`answers-unevidenced\\`,\n \\`record-federated\\`, \\`record-unreadable\\` and \\`record-raced\\`. Two records for\n different services land in either order — the later one waits, then merges,\n and both attestations survive.\n- \\`loam open\\`: \\`no-members\\`, \\`binding-duplicate\\`, \\`already-exists\\` (the\n workspace file, which loam never silently overwrites — pass \\`--out\\` or\n \\`--force\\`) and \\`invalid-option\\`.\n- \\`loam seed --from fleet.yaml\\`: \\`seed-file-invalid\\`, \\`seed-duplicate-service\\`,\n \\`seed-unknown-subsystem\\`, \\`seed-landscape-edited\\`, and \\`unknown-service\\` for a\n call endpoint nothing declares. An existing service directory is never moved,\n and nothing is ever deleted.\n- \\`internal\\` — an unexpected throw, the one code with no stable meaning.\n\n\\`--all\\` reports a target per service, a target per feature in flight, and one target\nof kind \\`landscape\\` for the fleet-level checks that belong to no single service.\n\nThree different words for three different questions, and no command conflates them:\n\\`ok\\` — the command ran; \\`valid\\` — the documents pass (\\`validate\\`); \\`verified\\` —\nsomebody says the code was built and showed evidence (\\`verify\\`). A feature can be\nvalid and unverified, or verified and incoherent. Read the one you meant.\n`;\n\n/* The archive gate, unarchive, and dropping a feature continue in\n * ./shipped/archive-gate.ts — one document section split at its own subject\n * seam, concatenated after this one by ../agents-md.ts. */\n"]}
1
+ {"version":3,"file":"refusals.js","sourceRoot":"","sources":["../../../../src/core/agent/agents-md/refusals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgOvB,CAAC;AAEF;;;oCAGoC","sourcesContent":["/**\n * What loam REFUSES: the OpenSpec migration surface, the containment and\n * unreadable-file findings, and the error-envelope codes. What happens to a\n * SHIPPED change — the archive gate, unarchive, and dropping a feature — used\n * to be a continuation section beside this one, split off when the two\n * subjects had grown past one file's ceiling. Nothing assembled it into\n * anything loam shipped once AGENTS.md became ../agents-md.ts's ORIENTATION,\n * and it was deleted: that teaching is the `loam-ship` protocol\n * (../workflows/closing.ts) and ORIENTATION's own \"Reading loam's output\" and\n * \"Safety and hand-back\" sections.\n *\n * Codes here are LISTED by the command that raises them, not glossed; the\n * meanings come from `loam explain <code>`, out of the running binary. See\n * ./command-map.ts's header for why the duplicated gloss left.\n *\n * THE ONE EXCEPTION IS THE FIRST BULLET. `loam explain` cannot answer\n * `openspec.*` or `mapping.*` — those commands grade a repository still in\n * another tool's shape, before it has a governed loop to look a code up from,\n * and the catalogue leaves them unanswered for that reason. Pointing this\n * section's reader at an explanation that does not exist is the exact failure\n * the pointer exists to prevent, so the migration notes stay written out here\n * until the catalogue reaches them. test/agent-contract.test.ts enforces the\n * boundary from the other side: every OTHER code backticked in AGENTS_MD must\n * resolve, so a family can never quietly lose its gloss and its explanation\n * at once.\n *\n * The last part of the `loam-codes` page, not a section of AGENTS.md any more:\n * ../workflows/reference/codes.ts assembles that page by PLAIN CONCATENATION —\n * no join separator — so this string starts at the first character of its\n * opening line and ends with the newline that closes its last one. Keep that\n * shape when editing, or two parts glue onto one line on the printed page.\n */\nexport const REFUSALS = `- \\`loam audit-openspec <root>\\` is the read-only OpenSpec inventory; an explicit\n \\`--write-mapping\\` writes a non-overwriting, digest-bound decision skeleton\n outside the source tree. \\`loam migrate-openspec <root> --map <file>\\` validates\n that mapping and stays dry-run unless both \\`--apply\\` and an empty \\`--target\\`\n are supplied. Living/active shape diagnostics are \\`openspec.workspace-empty\\`\n (the audited root holds no living spec and no active change, so there is\n nothing to migrate and no verdict over it can be \\`ready\\`),\n \\`openspec.specs-missing\\`,\n \\`openspec.config-invalid\\`, \\`openspec.external-store-pointer\\`,\n \\`openspec.living-empty\\`, \\`openspec.living-requirements-outside-section\\`,\n \\`openspec.living-delta-section\\`, \\`openspec.nonstandard-living-spec\\` (markdown\n under \\`specs/\\` named neither spec.md nor design.md, so no capability reads\n it), \\`openspec.change-metadata-invalid\\`,\n \\`openspec.change-schema-unresolved\\`, \\`openspec.skip-specs-with-specs\\`,\n \\`openspec.change-no-specs\\`, \\`openspec.change-empty\\`,\n \\`openspec.change-without-delta-sections\\`,\n \\`openspec.change-requirements-outside-delta-sections\\`,\n \\`openspec.change-quoted-requirements\\` (requirements under a \\`## Requirements\\`\n heading INSIDE a change delta — the shape OpenSpec's own living-spec template\n mandates, and one that stages nothing, so loam refuses it rather than counting\n requirements it cannot route),\n \\`openspec.hidden-change-directory\\` (a dot-prefixed directory under\n \\`changes/\\` is not enumerated as a change, so nothing under it migrates —\n rename it or move it out; under \\`changes/archive/\\` the same code is an archive\n diagnostic and does not gate, because frozen history never blocks),\n \\`openspec.nonstandard-change-spec\\`, \\`openspec.renamed-malformed\\`,\n \\`openspec.requirement-id-invalid\\`, \\`openspec.requirement-id-repeated\\`,\n \\`openspec.requirement-id-duplicate\\` (a stable \\`Requirement-ID\\` that is\n malformed, declared twice, or shared by two requirements — identity is never\n inferred from an invalid declaration, so the RENAMED chain cannot be followed\n through it), \\`openspec.symlink-unsupported\\`, and \\`openspec.non-utf8-artifact\\`;\n the same shapes in frozen history are non-blocking archive diagnostics.\n Mapping refuses stale or incomplete decisions under \\`mapping.source-missing\\`,\n \\`mapping.source-root-mismatch\\`, \\`mapping.source-digest-mismatch\\`,\n \\`mapping.unknown-capability\\`, \\`mapping.unknown-requirement\\`,\n \\`mapping.requirement-allocation-missing\\`,\n \\`mapping.requirement-service-unknown\\`, \\`mapping.service-allocation-empty\\`,\n \\`mapping.unknown-change\\`, \\`mapping.change-title-missing\\`,\n \\`mapping.feature-id-invalid\\`, \\`mapping.feature-id-duplicate\\`,\n \\`mapping.unknown-rename\\`, \\`mapping.invalid-requirement-id\\`,\n \\`mapping.rename-source-missing\\`, \\`mapping.rename-source-ambiguous\\`,\n \\`mapping.rename-source-id-invalid\\`, \\`mapping.rename-target-conflict\\`,\n \\`mapping.rename-existing-id-conflict\\`, \\`mapping.rename-id-conflict\\`,\n \\`mapping.rename-double-source\\`, \\`mapping.rename-double-target\\`,\n \\`mapping.rename-chain\\`,\n \\`mapping.unknown-artifact\\`, and \\`mapping.invalid-artifact-disposition\\`.\n- Repository containment failures are explicit: \\`sources.path-outside\\` rejects a\n provenance source outside the service repo and \\`gherkin.path-outside\\` rejects a\n generated-suite target outside it. Federated verify recording refuses under\n \\`service-mismatch\\`, \\`unknown-service\\` or \\`repository-unavailable\\` when the\n selected service, fleet identity and repository commit cannot be bound safely,\n and under \\`record-federated\\` when the legacy all-at-once form would erase other\n repositories' attestations. \\`loam gherkin --service <id>\\` and\n \\`loam vouch --service <id>\\` answer that same wrong-repo pair\n (\\`repository-unavailable\\`, \\`service-mismatch\\`) for the same reason. Neither is\n \\`invalid-option\\`: the flags were right and the directory was wrong, so the fix\n is to re-run in the service's own checkout, never to re-read the invocation.\n- A file loam cannot read is reported, never skipped: \\`service.unreadable\\` and\n \\`feature.unreadable\\` (both errors) name the path and say that nothing about\n that target was checked while the rest of the fleet still was. The whole docs\n repo being unreachable is a refusal instead: \\`docs-missing\\` or\n \\`services-missing\\`.\n- Both modes read frontmatter (\\`frontmatter.missing\\`, \\`frontmatter.malformed\\`,\n \\`frontmatter.field-mismatch\\`,\n \\`frontmatter.status-unknown\\`, \\`frontmatter.field-missing\\`, and\n \\`frontmatter.owner-mismatch\\` when one service's spec.md and arch.spec.md name two\n different owners); a service's spec.md —\n and its arch.spec.md, same conventions — additionally carries the sources chain\n (\\`sources.absent\\`, \\`sources.path-missing\\`, \\`sources.empty\\`, \\`sources.skipped\\`,\n \\`sources.unvouched\\`, \\`sources.stale\\`) plus the two doc-side checks,\n \\`content.stale\\` and \\`sources.sampled-vouch\\`, which need no service repo and so\n fire from the docs repo too.\n- A service target run from the service's own repository also re-checks the\n \\`evidence_pins\\` its federated verification records carry, against the working\n tree: \\`evidence.checked\\` and \\`evidence.unpinned\\` are the confirmations,\n \\`evidence.unresolved\\`, \\`evidence.moved\\`, \\`evidence.line-changed\\`,\n \\`evidence.token-missing\\` and \\`evidence.record-unreadable\\` the warnings.\n Demote-only, by doctrine: a warn here is a reading priority for a reviewer,\n never a verdict change — no pin state moves \\`attested\\` or \\`verified\\` in\n either direction. From the docs repo the service target's family is silent;\n \\`sources.unverifiable-from-here\\` already names that blind spot. The record\n itself lives in the docs repo, so the FEATURE target (\\`--feature\\`, and every\n active feature under \\`--all\\`) raises \\`evidence.record-unreadable\\` from\n anywhere — the same warn, about readability only.\n- \\`loam archive\\` alone reports the breaches only the merge computation can see:\n \\`living.requirement-outside-requirements\\`, \\`openapi.op-modified\\`,\n \\`openapi.path-item-modified\\`, \\`openapi.component-modified\\`,\n \\`openapi.ref-unresolved\\`, \\`openapi.remove-marker-path-level\\`, the event\n axis's own trio \\`asyncapi.message-modified\\` / \\`asyncapi.channel-modified\\` /\n \\`asyncapi.operation-modified\\`, \\`asyncapi.ref-unresolved\\`,\n \\`asyncapi.remove-marker-inline\\` and \\`service.no-model\\`. Two of those are\n graded at validate time as well and are STRICTER here —\n \\`openapi.ref-unresolved\\` and \\`asyncapi.ref-unresolved\\` are warnings there and\n errors here, because the merged document is what would carry the dangling\n reference — so \\`loam explain\\` on either prints both contexts.\n\n\\`loam validate <target>\\` is the positional spelling of the first two: a feature id\nor a service id, tried in that order, so the feature wins when one name could be\nboth and \\`--service\\`/\\`--feature\\` force the reading. When the name really is both,\nthe run says so — \\`target.ambiguous\\` (warn) names the reading it took and the\nflag that forces the other, and \\`--json\\` carries \\`resolvedKind\\` — because a\nsilent precedence rule is a report about a target nobody asked for. The\npositional together with \\`--all\\`, \\`--service\\` or \\`--feature\\` is refused\n(\\`invalid-option\\`).\n\nTwo rendering levers change nothing else: \\`loam validate --errors-only\\` drops\nthe confirmations from the TEXT view (the \\`--json\\` payload is byte-identical),\nand \\`loam list --needs-work\\` narrows the service list to the ones with\nsomething missing — the adoption worklist. \\`loam list --needs-work --review-order\\`\norders that worklist by fan-in — the services the most other services depend on\nfirst: drawn call edges into each, plus event subscriptions (a drawn \\`consumes\\`\nedge, or a living \\`Consumes:\\` line naming a message it sends) — a count, never\na priority judgement — and its \\`--json\\` rows then additionally carry \\`fanIn\\`\nand \\`reviewRank\\`; without \\`--needs-work\\` the flag is refused (\\`invalid-option\\`).\n\nTwo campaign flags slice that same services section and change nothing about\nany row. \\`loam list --subsystem <name>\\` limits the listing to the services\nfiled under one subsystem, at any depth: \\`services[]\\`, the \\`maturity\\` rollup\nand \\`subsystems[]\\` reflect the slice (\\`unfiledServices\\` is omitted — it is a\nfleet-root fact), while every per-row fact — \\`fanIn\\`, \\`apiExpected\\`,\n\\`missing\\` — is still computed fleet-wide, so filtering changes which rows\nappear, never what a row says; with \\`--review-order\\` the filter applies first\nand \\`reviewRank\\` stays contiguous within the filtered worklist. The name\n\\`unfiled\\` selects the services filed under no subsystem while nothing in the\ntree claims that name (a real subsystem or service spelled \\`unfiled\\` wins).\nAn unknown name refuses \\`unknown-target\\` with close-name hints; a service\nname refuses \\`invalid-option\\`. \\`loam list --owners <path>\\` joins each listed\nservice's directory to the owning teams in the named CODEOWNERS file —\ndirectory-pattern rules only, last match wins, an owner-less directory rule\nCLEARING ownership for what it matches exactly as the forge reads it; a\nrecognised rule outside that subset (any other wildcard use, \\`*\\` included)\nis reported under \\`skippedRules\\`, never guessed at — and \\`--json\\` gains the additive \\`owners\\`\nkey (\\`path\\`, \\`teams[]\\` with each team's services in the listing's own\nfiltered-and-ordered row order, \\`unowned[]\\` listing the rows no rule matched,\n\\`skippedRules[]\\`), so the per-team arrays are the per-team campaign\nworklists. A CODEOWNERS path that cannot be read, or a line that cannot be\nparsed as \\`pattern owner…\\`, refuses \\`owners-unreadable\\` naming the path and\nline — fail-closed, exactly as \\`answers-unreadable\\` treats the other\nuser-named file. Either flag with the \\`features\\`, \\`capabilities\\`, \\`glossary\\`\nor \\`suites\\` section is refused (\\`invalid-option\\`), as is \\`--needs-work\\` with\n\\`features\\` or \\`suites\\`, and a \\`[name]\\` after any section but \\`suites\\`.\n\n\\`--strict\\` (every targeting mode, \\`--all\\` included) exits 1 when any error\nor warning exists — \\`ok\\`-severity findings are confirmations and never trip\nit. It changes the exit code and nothing else:\n\\`valid\\` still means \"no errors\", and the \\`--json\\` payload stays byte-for-byte\nwhat it was. The stricter grade is a per-invocation lever, visible in the CI\npipeline that passes the flag — deliberately not a per-repo profile.\n\n\\`loam delta <FEAT> --json\\` exits 1 when either authored document behind the brief\nfailed to parse — \\`architecture.errors\\` non-empty, or \\`openapi.unreadable\\` true —\nwith \\`ok: true\\` and the full payload intact: the empty C4 slice means the delta\ndid not parse, and an empty \\`api\\` beside \\`openapi.unreadable\\` means the contract\ndelta did not parse, neither of them \"this feature changes nothing there\". Branch\non the exit code before consuming either slice as a task brief.\n\nFindings with severity \\`ok\\` are confirmations, not work: \\`c4.valid\\`, \\`delta.valid\\`,\n\\`requirements.covered\\`, \\`api.covered\\`, \\`spine.resolved\\`, \\`event.covered\\`, \\`coherence.ok\\`,\n\\`landscape.matched\\`, \\`archedge.covered\\`, \\`sources.resolved\\`, \\`sources.current\\`,\n\\`sources.walked\\`, \\`gherkin.current\\`.\n\nA finding's \\`subject\\` names the service it is about. The envelope separates \\`ok\\` (the\ncommand ran) from \\`valid\\` (the docs pass). A refusal is \\`ok: false\\` with a stable\n\\`error.code\\`, and \\`loam explain <code>\\` has the meaning of every one of them. What\nfollows is the other half — which command raises which:\n\n- Before any command can read anything: \\`no-config\\`, \\`config-invalid\\`,\n \\`docs-missing\\`, \\`services-missing\\`, \\`unknown-target\\`, \\`invalid-option\\`.\n \\`invalid-option\\` covers flags that contradict each other AND a value that\n cannot be right — a \\`loam list\\` section that is not services or features, or a\n service id that is not a legal \\`services/<id>/\\` directory name.\n- Every JOURNALED writer — \\`archive\\`, \\`unarchive\\`, \\`rebase\\`, \\`vouch\\`, \\`new\\`,\n \\`gherkin\\`, \\`seed\\`, \\`subsystem\\` and \\`verify --record\\` — shares one transaction\n and therefore one set of refusals: \\`docs-busy\\`, \\`commit-interrupted\\`,\n \\`merge-failed\\` and \\`rollback-incomplete\\`. Learn them once and you have read\n them for all nine. \\`loam doctor\\` names the interrupted state as\n \\`doctor.commit-interrupted\\` and \\`loam validate\\` leads every mode with\n \\`docs.commit-interrupted\\` while the journal sits there; every journaled writer\n recovers a finished predecessor's journal on its next run and reports it as\n \\`recovered\\` in \\`--json\\`.\n- \\`loam new\\`: \\`already-exists\\` — also the answer when a concurrent \\`new\\` for the\n same id wins the race.\n- \\`loam vouch\\`: \\`sources-absent\\`, \\`sources-path-missing\\`, \\`vouch-raced\\`, and the\n wrong-repo pair \\`repository-unavailable\\` / \\`service-mismatch\\`, because\n \\`sources\\` resolve against the service's own checkout and from anywhere else\n those paths are somebody else's.\n- \\`loam archive\\`: \\`not-coherent\\`, \\`living-outside-requirements\\`,\n \\`archive-exists\\` — see the archive gate below.\n- \\`loam unarchive\\`: \\`feature-active\\`, \\`snapshot-missing\\`, \\`snapshot-stale\\`,\n \\`snapshot-corrupt\\`, \\`restore-failed\\` — see \"Taking an archive back\".\n- \\`loam gherkin\\`: \\`gherkin-conflict\\`, plus the same wrong-repo pair, refused\n before anything is written because the generated files land in the repo loam\n is standing in.\n- \\`loam subsystem\\`: \\`subsystem-not-empty\\`, \\`move-uncommitted\\`, \\`move-failed\\`,\n and \\`unknown-target\\` / \\`already-exists\\` / \\`invalid-option\\` for names that\n resolve to nothing, collide in the flat namespace, or break the grammar.\n \\`move-failed\\` is deliberately distinct from \\`merge-failed\\`: the move's own\n transaction rolled back cleanly, nothing changed, and re-running can work.\n- \\`loam verify --record\\` / \\`--results\\` / \\`--contract-results\\`:\n \\`answers-unreadable\\`, \\`answers-mismatch\\`, \\`answers-unevidenced\\`,\n \\`record-federated\\`, \\`record-unreadable\\` and \\`record-raced\\`. Two records for\n different services land in either order — each writes only its own\n \\`verification/<svc>.yaml\\`, so both attestations survive, on one checkout or\n on two docs-repo branches merged later. A \\`record-unreadable\\` names the file\n that failed and its safe repair; never delete the one-file\n \\`verification.yaml\\`, which may hold other services' attestations.\n- \\`loam open\\`: \\`no-members\\`, \\`binding-duplicate\\`, \\`already-exists\\` (the\n workspace file, which loam never silently overwrites — pass \\`--out\\` or\n \\`--force\\`) and \\`invalid-option\\`.\n- \\`loam seed --from fleet.yaml\\`: \\`seed-file-invalid\\`, \\`seed-duplicate-service\\`,\n \\`seed-unknown-subsystem\\`, \\`seed-landscape-edited\\`, \\`already-exists\\` when a\n generated views file in \\`architecture/\\` was written by somebody rather than by\n loam (no \\`// generated by loam\\` first line — seed writes nothing at all in that\n case), and \\`unknown-service\\` for a call endpoint nothing declares. An existing\n service directory is never moved, and nothing is ever deleted.\n- \\`internal\\` — an unexpected throw, the one code with no stable meaning.\n\n\\`--all\\` reports a target per service, a target per feature in flight, and one target\nof kind \\`landscape\\` for the fleet-level checks that belong to no single service.\n\nThree different words for three different questions, and no command conflates them:\n\\`ok\\` — the command ran; \\`valid\\` — the documents pass (\\`validate\\`); \\`verified\\` —\nsomebody says the code was built and showed evidence (\\`verify\\`). A feature can be\nvalid and unverified, or verified and incoherent. Read the one you meant.\n`;\n\n/* The archive gate, unarchive, and dropping a feature are not continued here:\n * the section that once followed this one was never assembled again after\n * AGENTS.md became ORIENTATION, and was deleted — see this file's header for\n * where that teaching ships now. */\n"]}
@@ -18,19 +18,29 @@ loam has no server or database. Delete it and the documents still make sense.
18
18
 
19
19
  \`architecture/landscape.likec4\` is the fleet map, and
20
20
  \`architecture/adrs/\` optional system-level decisions — only their links are graded.
21
- \`architecture/subsystems.likec4\` is GENERATED by \`loam subsystem sync\` from the
22
- directory tree under \`services/\` — one view per subsystem, a scoping aid, never
23
- hand-edited and never a second fleet map; its one styling opt-in is a
24
- \`global { styleGroup subsystems { … } }\` declared in any \`.likec4\` under
25
- \`architecture/\`, which \`sync\` then references. A diagram you author belongs in the
26
- landscape or in a file of your own name beside it.
21
+ \`architecture/subsystems.likec4\` and \`architecture/services.likec4\` are both
22
+ GENERATED by \`loam subsystem sync\` — one view per subsystem from the directory
23
+ tree under \`services/\`, and one overview view per service the fleet map draws.
24
+ Scoping aids, never hand-edited and never a second fleet map; each is stale
25
+ (\`subsystem.views-stale\`, one finding per file) until the next \`sync\`. Their one
26
+ styling opt-in is a \`global { styleGroup subsystems { … } }\` — or
27
+ \`styleGroup services\` — declared in any \`.likec4\` under \`architecture/\`, which
28
+ \`sync\` then references. A diagram you author belongs in the landscape or in a
29
+ file of your own name beside it; an authored view \`of\` an element the generated
30
+ file also draws is legal, and the file that sorts first wins the default
31
+ drill-down target while both stay reachable.
27
32
  \`services/<id>/\` holds a boundary's living \`model.likec4\`, \`spec.md\`,
28
33
  \`arch.spec.md\`, OpenAPI, AsyncAPI, runbook and health facts — and
29
34
  \`services/<…>/<id>/usecases/*.likec4\`, the slot for a \`dynamic view\` over that
30
35
  service's OWN containers, tagged \`#req-<Requirement-ID>\`; a flow that crosses
31
- services lives in \`architecture/usecases/\` and is tagged \`#cap-\` instead.
36
+ services lives in \`architecture/usecases/\` and is tagged \`#cap-\` instead. Title
37
+ a service use case \`'<subsystem path> / <service id> / <use case>'\` — LikeC4
38
+ splits a view title on \`/\` into browser folders, so that convention lands the
39
+ flow in the same folder as the generated overview of its service. Nothing grades
40
+ the title, and nothing grades the slot's absence: presence is never owed.
32
41
  \`features/<FEAT>/\` holds a change in flight: \`intent.md\`, \`delta.likec4\`,
33
- per-service requirement and contract deltas, and \`verification.yaml\`.
42
+ per-service requirement and contract deltas, and the verification record —
43
+ \`verification/<svc>.yaml\`, one file per attesting service.
34
44
  \`features/archive/\` is shipped history.
35
45
 
36
46
  Living documents are the complete current state. A feature document is a diff
@@ -91,7 +101,9 @@ seventh lifecycle step.
91
101
  ## The cycle
92
102
 
93
103
  0. **Wire the repo** — in each service checkout run
94
- \`loam init --docs <path-to-docs-repo> --service <id>\`, then \`loam doctor\`.
104
+ \`loam init --docs <path-to-docs-repo> --service <id> --agent-profile service\`,
105
+ then \`loam doctor\`. The service profile writes the five workflows that run
106
+ beside the code; \`loam-feature\` and \`loam-ship\` stay in the docs repo.
95
107
  1. **Adopt** — for an undocumented boundary load \`loam instructions loam-adopt
96
108
  <id>\`, then use \`loam adopt --service <id> --json\`. Write drafts; a person,
97
109
  never an agent, later vouches for them. Draw the map's edges in the same pass:
@@ -112,7 +124,10 @@ seventh lifecycle step.
112
124
  \`loam validate <FEAT> --json\`, and use \`loam explain <code>\` for reported
113
125
  codes. \`loam dependencies --json\` names features that must archive first.
114
126
  6. **Build** — load \`loam instructions loam-implement <FEAT>\`.
115
- \`loam delta <FEAT> --service <id> --json\` is one service's task; in that
127
+ \`loam delta <FEAT> --service <id> --json\` is one service's task;
128
+ \`loam tasks <FEAT> --service <id> --next --json\` is the next open unit of it —
129
+ its \`at\` says whether you owe a scenario, a generated suite or code, and
130
+ \`done.check\` is the fact that closes it. In that
116
131
  service checkout, \`loam gherkin <FEAT>\` emits its executable scenarios.
117
132
  7. **Verify** — \`loam verify <FEAT> --json\` is the checklist. Record evidence
118
133
  from each affected service's own checkout. Read
@@ -1 +1 @@
1
- {"version":3,"file":"agents-md.js","sourceRoot":"","sources":["../../../src/core/agent/agents-md.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEtD,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2KnB,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,eAAe,CAAC,YAAY,CAAC,KAAK,WAAW,EAAE,CAAC","sourcesContent":["/**\n * The small, always-loaded orientation contract for a loam docs repository.\n *\n * Detailed grammars and workflows live behind `loam instructions`: those pages\n * describe the binary that is about to run, while this file is written once and\n * may be loaded into every agent turn for years. Keep only enough here to form\n * the next question, find the relevant page, and avoid an unsafe write.\n */\nimport { agentsStampLine } from \"./agents-stamp.js\";\nimport { LOAM_VERSION } from \"../envelope/version.js\";\n\nconst ORIENTATION = `# Working in this docs repo\n\nThis is a **loam** docs repo: plain files are the shared source of truth for a governed software\nfleet's architecture, requirements, contracts and verification evidence. A service may be a modular monolith, a network service, a CLI or a worker.\nloam has no server or database. Delete it and the documents still make sense.\n\n## What is where\n\n\\`architecture/landscape.likec4\\` is the fleet map, and\n\\`architecture/adrs/\\` optional system-level decisions — only their links are graded.\n\\`architecture/subsystems.likec4\\` is GENERATED by \\`loam subsystem sync\\` from the\ndirectory tree under \\`services/\\` — one view per subsystem, a scoping aid, never\nhand-edited and never a second fleet map; its one styling opt-in is a\n\\`global { styleGroup subsystems { … } }\\` declared in any \\`.likec4\\` under\n\\`architecture/\\`, which \\`sync\\` then references. A diagram you author belongs in the\nlandscape or in a file of your own name beside it.\n\\`services/<id>/\\` holds a boundary's living \\`model.likec4\\`, \\`spec.md\\`,\n\\`arch.spec.md\\`, OpenAPI, AsyncAPI, runbook and health facts — and\n\\`services/<…>/<id>/usecases/*.likec4\\`, the slot for a \\`dynamic view\\` over that\nservice's OWN containers, tagged \\`#req-<Requirement-ID>\\`; a flow that crosses\nservices lives in \\`architecture/usecases/\\` and is tagged \\`#cap-\\` instead.\n\\`features/<FEAT>/\\` holds a change in flight: \\`intent.md\\`, \\`delta.likec4\\`,\nper-service requirement and contract deltas, and \\`verification.yaml\\`.\n\\`features/archive/\\` is shipped history.\n\nLiving documents are the complete current state. A feature document is a diff\nagainst them, reviewed as a diff; \\`loam archive\\` folds it into living state.\nDo not hand-edit the living landscape to add a feature change.\n\n## Linking between documents\n\nDocuments join with **standard markdown links** whose targets are real relative\npaths, for example \\`[decision](../adrs/ADR-001.md)\\`. They render in pull-request\nreview instead of showing literal brackets,\nand make “does this link resolve” a filesystem question; \\`[[Wikilinks]]\\` would\nrequire shortest-unique-path guessing.\nObsidian supports markdown links, autocomplete and rename-tracking when its\nwikilink setting is off. \\`link.unresolved\\` grades missing targets and CASE\n**is** graded; links outside this repository, \\`#section\\` targets, a fenced code\nblock or an inline code span are deliberately not graded.\n\n## \\`loam.json\\` — the wiring\n\nEvery repository commits its own \\`loam.json\\`:\n\n\\`{ \"docsDir\": \"../docs\", \"service\": \"payment-service\", \"gherkinDir\": \"features\" }\\`\n\n\\`docsDir\\` is stored exactly as it was passed and resolved against the directory\nholding the \\`loam.json\\`, so a relative path survives a clone; an absolute one\nraises \\`doctor.docs-absolute\\`. \\`service\\` binds a service checkout to the one boundary\nit may write evidence for. \\`gherkinDir\\` is optional. \\`agentTools\\` and\n\\`agentProfile\\` record which agent surfaces and workflow subset init manages;\n\\`agentFiles\\` records digests so unchanged generated pointers can refresh while\ncustomized ones remain untouched. With no config, loam refuses with \\`no-config\\`\nrather than guessing.\n\n\\`loam init --docs <dir>\\` **joins** an existing docs repo. Add \\`--create\\` only\nwhen creating a new one; a typo must not scaffold a second source of truth.\n\n## Starting from chat\n\nThere are two equivalent entry points into the same version-matched protocol.\nPrefer the explicit one when a user wants a predictable lifecycle: invoke\n\\`/loam-feature <FEAT> \"<title>\"\\` to propose, \\`/loam-implement <FEAT> [service]\\`\nto build, \\`/loam-check\\` and \\`/loam-verify <FEAT>\\` to grade it, and\n\\`/loam-ship <FEAT>\\` to finish. Most hosts use that flat slash\nspelling; Gemini uses \\`/loam:feature\\`, Amazon Q uses \\`@loam-feature\\`, and a\nskills-only host can invoke \\`$loam-feature\\`. Natural-language requests may load\nthe matching Agent Skill instead. Once either entry point is selected, the agent\ndrives the internal status/edit/validate loop and asks the user only for a real\ndecision or human-only act; the user does not have to type every loam command.\n\nIf loam or an agent integration behaves unexpectedly, invoke \\`/loam-report\\` (or\nload \\`$loam-report\\` on a skills-only host). It collects a sanitized, reproducible\nMarkdown report under \\`loam-reports/\\` without repairing the repository, retrying a\nwriter or uploading anything. Reports are numbered — \\`loam-reports/NNN-YYYY-MM-DD-<slug>.md\\`,\ncited by ordinal — and carry a \\`Status:\\` line, so \\`loam doctor\\` counts the\ndirectory and says which ordinal the next report takes. This is support, not a\nseventh lifecycle step.\n\n## The cycle\n\n0. **Wire the repo** — in each service checkout run\n \\`loam init --docs <path-to-docs-repo> --service <id>\\`, then \\`loam doctor\\`.\n1. **Adopt** — for an undocumented boundary load \\`loam instructions loam-adopt\n <id>\\`, then use \\`loam adopt --service <id> --json\\`. Write drafts; a person,\n never an agent, later vouches for them. Draw the map's edges in the same pass:\n the brief's \\`landscape.touched\\` is the only place an edgeless element is named,\n and \\`landscape.service-isolated\\` (fleet scope) goes quiet on the first edge.\n2. **Understand** — start or resume with \\`loam status --json\\`; its ordered\n \\`next[]\\` is the work queue. Read \\`next[0].execution\\`: run it only when\n \\`runnable\\` is true; otherwise use \\`kind\\`, \\`cwd\\`, \\`needs\\` and \\`after\\`\n to complete the edit or external-repo step. Use \\`loam list --json\\`, \\`loam show <id> --json\\`\n and \\`loam explore <id> --json\\` before proposing a change. Never propose a\n change to a service you have not read.\n3. **Scaffold** — load \\`loam instructions loam-feature <FEAT>\\`, then run\n \\`loam new <FEAT> --title \"...\" --touches <service>\\`.\n4. **Author** — fill the scaffolded TODOs and run \\`loam rebase <FEAT>\\` after\n reading the living baselines. The exact joins and authoring grammars are in\n the reference pages below.\n5. **Check** — load \\`loam instructions loam-check --no-fix-tables\\`, run\n \\`loam validate <FEAT> --json\\`, and use \\`loam explain <code>\\` for reported\n codes. \\`loam dependencies --json\\` names features that must archive first.\n6. **Build** — load \\`loam instructions loam-implement <FEAT>\\`.\n \\`loam delta <FEAT> --service <id> --json\\` is one service's task; in that\n service checkout, \\`loam gherkin <FEAT>\\` emits its executable scenarios.\n7. **Verify** — \\`loam verify <FEAT> --json\\` is the checklist. Record evidence\n from each affected service's own checkout. Read\n \\`loam instructions loam-done-check\\` before recording anything.\n8. **Ship** — load \\`loam instructions loam-ship <FEAT>\\`; begin with\n \\`loam archive <FEAT> --dry-run --json\\`, then archive only after the code is\n merged and the plan is the one reviewed.\n\n## Reading loam's output\n\nEvery command takes \\`--json\\`. The envelope has \\`contractVersion\\`, \\`ok\\` and,\non refusal, \\`error.code\\`. Unknown flags are \\`invalid-option\\`; \\`--help\\` and\n\\`--version\\` pass through. Branch on stable codes, never message prose.\n\nValidation \\`findings[]\\` carry \\`severity\\`; archive-related findings also carry\n\\`gates\\`. Severity and gating are two different questions: errors invalidate,\nwhile \\`gates: true\\` makes archive refuse — archive is gated on coherence, so a\ngating finding stops it whatever its severity. Advisory warnings never block.\n\\`--approve\\` overrides the gating issues — only those that are judgement calls —\nand never a mechanical loss. For example \\`delta.requirement-not-merged\\` warns but gates;\n\\`living.requirement-outside-requirements\\` cannot be approved, while\n\\`openapi.op-modified\\` is reviewable.\n\n\\`loam status --json\\` orders executable next actions. Findings include structured\n\\`locations[]\\`; use their paths instead of extracting filenames from prose.\n\\`loam show <FEAT> --json\\` includes a \\`review\\` pack with intent, architecture,\ncontracts, dependencies, verification, blockers and the same next actions.\n\\`loam explain <code> --json\\`\nreturns one code's meaning and fix; \\`loam explain --codes --json\\` lists them.\nWhich codes an INVOCATION can raise is in \\`loam instructions loam-codes\\`.\n\n## The identity rule\n\nA service is the element whose \\`metadata { service '<id>' }\\` matches its\n\\`services/<id>/\\` directory. An exact element id is the legacy fallback. Never\ninfer identity from display names or containment. Relationships join to contracts\nthrough exact ids: OpenAPI \\`operationId\\`, requirement \\`Operations:\\`, and C4\n\\`metadata { op '...' }\\`. Consult \\`loam instructions loam-spine\\` before editing\na join.\n\n## Reference pages — fetch when needed\n\n- \\`loam instructions loam-codes\\` — finding/refusal inventory and fixes.\n- \\`loam instructions loam-spine\\` — operation, message, coverage, permission,\n capability and baseline joins.\n- \\`loam instructions loam-authoring\\` — arch requirements, generated Gherkin\n and frontmatter/vouch grammar.\n- \\`loam instructions loam-done-check\\` — verification evidence and verdicts.\n\nBare \\`loam instructions\\` lists the six workflows, the separate \\`loam-report\\`\nsupport protocol and these references. Generated agent command/skill files are\nsmall pointers to them, so the runtime page wins if an old pointer disagrees with\nthe installed binary. MCP clients can read the same pages as\n\\`loam://orientation\\` and \\`loam://instructions/<name>\\` resources.\n\n## Safety and hand-back\n\nloam's writers are journaled, but the lifecycle still matters. Do not run\n\\`loam vouch\\` for a human, do not invent evidence, do not archive before code is\nmerged, and do not edit generated files under \\`<gherkinDir>/loam/\\` by hand.\n\\`loam unarchive <FEAT>\\` is the supported undo. To abandon a feature that was\nnever archived, review it and use \\`git rm -r features/<FEAT>\\`; loam deliberately\nhas no destructive drop command.\n\nFinish by reporting remaining warnings, unverifiable claims, files or directories\nyou did not inspect, and the exact human action still owed.\n`;\n\nexport const AGENTS_MD = `${agentsStampLine(LOAM_VERSION)}\\n${ORIENTATION}`;\n"]}
1
+ {"version":3,"file":"agents-md.js","sourceRoot":"","sources":["../../../src/core/agent/agents-md.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEtD,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0LnB,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,eAAe,CAAC,YAAY,CAAC,KAAK,WAAW,EAAE,CAAC","sourcesContent":["/**\n * The small, always-loaded orientation contract for a loam docs repository.\n *\n * Detailed grammars and workflows live behind `loam instructions`: those pages\n * describe the binary that is about to run, while this file is written once and\n * may be loaded into every agent turn for years. Keep only enough here to form\n * the next question, find the relevant page, and avoid an unsafe write.\n */\nimport { agentsStampLine } from \"./agents-stamp.js\";\nimport { LOAM_VERSION } from \"../envelope/version.js\";\n\nconst ORIENTATION = `# Working in this docs repo\n\nThis is a **loam** docs repo: plain files are the shared source of truth for a governed software\nfleet's architecture, requirements, contracts and verification evidence. A service may be a modular monolith, a network service, a CLI or a worker.\nloam has no server or database. Delete it and the documents still make sense.\n\n## What is where\n\n\\`architecture/landscape.likec4\\` is the fleet map, and\n\\`architecture/adrs/\\` optional system-level decisions — only their links are graded.\n\\`architecture/subsystems.likec4\\` and \\`architecture/services.likec4\\` are both\nGENERATED by \\`loam subsystem sync\\` — one view per subsystem from the directory\ntree under \\`services/\\`, and one overview view per service the fleet map draws.\nScoping aids, never hand-edited and never a second fleet map; each is stale\n(\\`subsystem.views-stale\\`, one finding per file) until the next \\`sync\\`. Their one\nstyling opt-in is a \\`global { styleGroup subsystems { … } }\\` — or\n\\`styleGroup services\\` — declared in any \\`.likec4\\` under \\`architecture/\\`, which\n\\`sync\\` then references. A diagram you author belongs in the landscape or in a\nfile of your own name beside it; an authored view \\`of\\` an element the generated\nfile also draws is legal, and the file that sorts first wins the default\ndrill-down target while both stay reachable.\n\\`services/<id>/\\` holds a boundary's living \\`model.likec4\\`, \\`spec.md\\`,\n\\`arch.spec.md\\`, OpenAPI, AsyncAPI, runbook and health facts — and\n\\`services/<…>/<id>/usecases/*.likec4\\`, the slot for a \\`dynamic view\\` over that\nservice's OWN containers, tagged \\`#req-<Requirement-ID>\\`; a flow that crosses\nservices lives in \\`architecture/usecases/\\` and is tagged \\`#cap-\\` instead. Title\na service use case \\`'<subsystem path> / <service id> / <use case>'\\` — LikeC4\nsplits a view title on \\`/\\` into browser folders, so that convention lands the\nflow in the same folder as the generated overview of its service. Nothing grades\nthe title, and nothing grades the slot's absence: presence is never owed.\n\\`features/<FEAT>/\\` holds a change in flight: \\`intent.md\\`, \\`delta.likec4\\`,\nper-service requirement and contract deltas, and the verification record —\n\\`verification/<svc>.yaml\\`, one file per attesting service.\n\\`features/archive/\\` is shipped history.\n\nLiving documents are the complete current state. A feature document is a diff\nagainst them, reviewed as a diff; \\`loam archive\\` folds it into living state.\nDo not hand-edit the living landscape to add a feature change.\n\n## Linking between documents\n\nDocuments join with **standard markdown links** whose targets are real relative\npaths, for example \\`[decision](../adrs/ADR-001.md)\\`. They render in pull-request\nreview instead of showing literal brackets,\nand make “does this link resolve” a filesystem question; \\`[[Wikilinks]]\\` would\nrequire shortest-unique-path guessing.\nObsidian supports markdown links, autocomplete and rename-tracking when its\nwikilink setting is off. \\`link.unresolved\\` grades missing targets and CASE\n**is** graded; links outside this repository, \\`#section\\` targets, a fenced code\nblock or an inline code span are deliberately not graded.\n\n## \\`loam.json\\` — the wiring\n\nEvery repository commits its own \\`loam.json\\`:\n\n\\`{ \"docsDir\": \"../docs\", \"service\": \"payment-service\", \"gherkinDir\": \"features\" }\\`\n\n\\`docsDir\\` is stored exactly as it was passed and resolved against the directory\nholding the \\`loam.json\\`, so a relative path survives a clone; an absolute one\nraises \\`doctor.docs-absolute\\`. \\`service\\` binds a service checkout to the one boundary\nit may write evidence for. \\`gherkinDir\\` is optional. \\`agentTools\\` and\n\\`agentProfile\\` record which agent surfaces and workflow subset init manages;\n\\`agentFiles\\` records digests so unchanged generated pointers can refresh while\ncustomized ones remain untouched. With no config, loam refuses with \\`no-config\\`\nrather than guessing.\n\n\\`loam init --docs <dir>\\` **joins** an existing docs repo. Add \\`--create\\` only\nwhen creating a new one; a typo must not scaffold a second source of truth.\n\n## Starting from chat\n\nThere are two equivalent entry points into the same version-matched protocol.\nPrefer the explicit one when a user wants a predictable lifecycle: invoke\n\\`/loam-feature <FEAT> \"<title>\"\\` to propose, \\`/loam-implement <FEAT> [service]\\`\nto build, \\`/loam-check\\` and \\`/loam-verify <FEAT>\\` to grade it, and\n\\`/loam-ship <FEAT>\\` to finish. Most hosts use that flat slash\nspelling; Gemini uses \\`/loam:feature\\`, Amazon Q uses \\`@loam-feature\\`, and a\nskills-only host can invoke \\`$loam-feature\\`. Natural-language requests may load\nthe matching Agent Skill instead. Once either entry point is selected, the agent\ndrives the internal status/edit/validate loop and asks the user only for a real\ndecision or human-only act; the user does not have to type every loam command.\n\nIf loam or an agent integration behaves unexpectedly, invoke \\`/loam-report\\` (or\nload \\`$loam-report\\` on a skills-only host). It collects a sanitized, reproducible\nMarkdown report under \\`loam-reports/\\` without repairing the repository, retrying a\nwriter or uploading anything. Reports are numbered — \\`loam-reports/NNN-YYYY-MM-DD-<slug>.md\\`,\ncited by ordinal — and carry a \\`Status:\\` line, so \\`loam doctor\\` counts the\ndirectory and says which ordinal the next report takes. This is support, not a\nseventh lifecycle step.\n\n## The cycle\n\n0. **Wire the repo** — in each service checkout run\n \\`loam init --docs <path-to-docs-repo> --service <id> --agent-profile service\\`,\n then \\`loam doctor\\`. The service profile writes the five workflows that run\n beside the code; \\`loam-feature\\` and \\`loam-ship\\` stay in the docs repo.\n1. **Adopt** — for an undocumented boundary load \\`loam instructions loam-adopt\n <id>\\`, then use \\`loam adopt --service <id> --json\\`. Write drafts; a person,\n never an agent, later vouches for them. Draw the map's edges in the same pass:\n the brief's \\`landscape.touched\\` is the only place an edgeless element is named,\n and \\`landscape.service-isolated\\` (fleet scope) goes quiet on the first edge.\n2. **Understand** — start or resume with \\`loam status --json\\`; its ordered\n \\`next[]\\` is the work queue. Read \\`next[0].execution\\`: run it only when\n \\`runnable\\` is true; otherwise use \\`kind\\`, \\`cwd\\`, \\`needs\\` and \\`after\\`\n to complete the edit or external-repo step. Use \\`loam list --json\\`, \\`loam show <id> --json\\`\n and \\`loam explore <id> --json\\` before proposing a change. Never propose a\n change to a service you have not read.\n3. **Scaffold** — load \\`loam instructions loam-feature <FEAT>\\`, then run\n \\`loam new <FEAT> --title \"...\" --touches <service>\\`.\n4. **Author** — fill the scaffolded TODOs and run \\`loam rebase <FEAT>\\` after\n reading the living baselines. The exact joins and authoring grammars are in\n the reference pages below.\n5. **Check** — load \\`loam instructions loam-check --no-fix-tables\\`, run\n \\`loam validate <FEAT> --json\\`, and use \\`loam explain <code>\\` for reported\n codes. \\`loam dependencies --json\\` names features that must archive first.\n6. **Build** — load \\`loam instructions loam-implement <FEAT>\\`.\n \\`loam delta <FEAT> --service <id> --json\\` is one service's task;\n \\`loam tasks <FEAT> --service <id> --next --json\\` is the next open unit of it —\n its \\`at\\` says whether you owe a scenario, a generated suite or code, and\n \\`done.check\\` is the fact that closes it. In that\n service checkout, \\`loam gherkin <FEAT>\\` emits its executable scenarios.\n7. **Verify** — \\`loam verify <FEAT> --json\\` is the checklist. Record evidence\n from each affected service's own checkout. Read\n \\`loam instructions loam-done-check\\` before recording anything.\n8. **Ship** — load \\`loam instructions loam-ship <FEAT>\\`; begin with\n \\`loam archive <FEAT> --dry-run --json\\`, then archive only after the code is\n merged and the plan is the one reviewed.\n\n## Reading loam's output\n\nEvery command takes \\`--json\\`. The envelope has \\`contractVersion\\`, \\`ok\\` and,\non refusal, \\`error.code\\`. Unknown flags are \\`invalid-option\\`; \\`--help\\` and\n\\`--version\\` pass through. Branch on stable codes, never message prose.\n\nValidation \\`findings[]\\` carry \\`severity\\`; archive-related findings also carry\n\\`gates\\`. Severity and gating are two different questions: errors invalidate,\nwhile \\`gates: true\\` makes archive refuse — archive is gated on coherence, so a\ngating finding stops it whatever its severity. Advisory warnings never block.\n\\`--approve\\` overrides the gating issues — only those that are judgement calls —\nand never a mechanical loss. For example \\`delta.requirement-not-merged\\` warns but gates;\n\\`living.requirement-outside-requirements\\` cannot be approved, while\n\\`openapi.op-modified\\` is reviewable.\n\n\\`loam status --json\\` orders executable next actions. Findings include structured\n\\`locations[]\\`; use their paths instead of extracting filenames from prose.\n\\`loam show <FEAT> --json\\` includes a \\`review\\` pack with intent, architecture,\ncontracts, dependencies, verification, blockers and the same next actions.\n\\`loam explain <code> --json\\`\nreturns one code's meaning and fix; \\`loam explain --codes --json\\` lists them.\nWhich codes an INVOCATION can raise is in \\`loam instructions loam-codes\\`.\n\n## The identity rule\n\nA service is the element whose \\`metadata { service '<id>' }\\` matches its\n\\`services/<id>/\\` directory. An exact element id is the legacy fallback. Never\ninfer identity from display names or containment. Relationships join to contracts\nthrough exact ids: OpenAPI \\`operationId\\`, requirement \\`Operations:\\`, and C4\n\\`metadata { op '...' }\\`. Consult \\`loam instructions loam-spine\\` before editing\na join.\n\n## Reference pages — fetch when needed\n\n- \\`loam instructions loam-codes\\` — finding/refusal inventory and fixes.\n- \\`loam instructions loam-spine\\` — operation, message, coverage, permission,\n capability and baseline joins.\n- \\`loam instructions loam-authoring\\` — arch requirements, generated Gherkin\n and frontmatter/vouch grammar.\n- \\`loam instructions loam-done-check\\` — verification evidence and verdicts.\n\nBare \\`loam instructions\\` lists the six workflows, the separate \\`loam-report\\`\nsupport protocol and these references. Generated agent command/skill files are\nsmall pointers to them, so the runtime page wins if an old pointer disagrees with\nthe installed binary. MCP clients can read the same pages as\n\\`loam://orientation\\` and \\`loam://instructions/<name>\\` resources.\n\n## Safety and hand-back\n\nloam's writers are journaled, but the lifecycle still matters. Do not run\n\\`loam vouch\\` for a human, do not invent evidence, do not archive before code is\nmerged, and do not edit generated files under \\`<gherkinDir>/loam/\\` by hand.\n\\`loam unarchive <FEAT>\\` is the supported undo. To abandon a feature that was\nnever archived, review it and use \\`git rm -r features/<FEAT>\\`; loam deliberately\nhas no destructive drop command.\n\nFinish by reporting remaining warnings, unverifiable claims, files or directories\nyou did not inspect, and the exact human action still owed.\n`;\n\nexport const AGENTS_MD = `${agentsStampLine(LOAM_VERSION)}\\n${ORIENTATION}`;\n"]}
@@ -4,6 +4,16 @@ export function agentsStampLine(version) {
4
4
  }
5
5
  /** Matches a stamp line anywhere in the file; the capture is the version text. */
6
6
  const STAMP_RE = /<!--\s*generated by loam v(\S+?)\s*-->/;
7
+ /**
8
+ * `text` with its stamp, when it has one, rewritten to name `version` — every
9
+ * other byte untouched. What lets a caller ask whether a generated file differs
10
+ * from what a binary would write in anything BUT the stamp: the pointer files
11
+ * carry this same line, and a release that changes nothing in their body still
12
+ * changes it.
13
+ */
14
+ export function restamped(text, version) {
15
+ return text.replace(STAMP_RE, agentsStampLine(version));
16
+ }
7
17
  /**
8
18
  * The version the stamp names, or null when there is no stamp (or one whose
9
19
  * version does not read as numbers — unparseable is indistinguishable from
@@ -1 +1 @@
1
- {"version":3,"file":"agents-stamp.js","sourceRoot":"","sources":["../../../src/core/agent/agents-stamp.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The AGENTS.md version stamp: detection, never refresh.\n *\n * AGENTS.md is written once by `loam init` and never touched again — that is\n * the ownership contract (agent.ts): it is a starting point, and a team's edits\n * to it outrank ours. But the file tells agents to branch on flag and code\n * tables, and those tables describe the binary that wrote them. OpenSpec hit\n * the same wall and answered with auto-refresh, which teaches everyone the file\n * belongs to the tool after all; loam answers with the smallest honest thing —\n * a version stamp in the generated file, and one warning when the stamp is\n * missing or trails the running binary. Reviewing the file and bumping the\n * stamp is a human act, and a hand-curated file silences the warning the same\n * way: by saying, in the stamp, that someone has looked.\n */\nimport type { Finding } from \"../vocabulary/report.js\";\n\n/** The stamp `loam init` writes at the top of AGENTS.md. */\nexport function agentsStampLine(version: string): string {\n return `<!-- generated by loam v${version} -->`;\n}\n\n/** Matches a stamp line anywhere in the file; the capture is the version text. */\nconst STAMP_RE = /<!--\\s*generated by loam v(\\S+?)\\s*-->/;\n\n/**\n * The version the stamp names, or null when there is no stamp (or one whose\n * version does not read as numbers — unparseable is indistinguishable from\n * absent, and both get the same advice).\n */\nexport function agentsStampVersion(text: string): string | null {\n const m = STAMP_RE.exec(text);\n return m !== null && parseVersion(m[1]!) !== null ? m[1]! : null;\n}\n\n/** A version split the way precedence is decided: the triple, then the prerelease identifiers. */\ninterface ParsedVersion {\n release: [number, number, number];\n /**\n * The dot-separated identifiers of a `-beta.2` suffix, empty for a final\n * release. Build metadata (`+sha`) is dropped — semver excludes it from\n * precedence, and two builds of one version are the same instructions.\n */\n pre: string[];\n}\n\n/** `X.Y.Z[-pre][+build]`, or null when the leading triple does not read as numbers. */\nfunction parseVersion(v: string): ParsedVersion | null {\n const m = /^(\\d+)\\.(\\d+)\\.(\\d+)(?:-([0-9A-Za-z.-]+))?(?:\\+[0-9A-Za-z.-]+)?$/.exec(v);\n if (m === null) return null;\n return {\n release: [Number(m[1]), Number(m[2]), Number(m[3])],\n pre: m[4] === undefined ? [] : m[4].split(\".\"),\n };\n}\n\n/**\n * Semver identifier precedence: numeric identifiers compare numerically and\n * rank below alphanumeric ones, and a longer identifier list outranks a prefix\n * of itself. -1 / 0 / 1, so the caller reads like the triple comparison above.\n */\nfunction comparePre(a: string[], b: string[]): number {\n // No prerelease outranks any prerelease — `1.2.0` is newer than `1.2.0-beta.9`\n // — which is why this cannot be the plain lexicographic walk the identifiers get.\n if (a.length === 0 || b.length === 0) return a.length === b.length ? 0 : a.length === 0 ? 1 : -1;\n for (let i = 0; i < Math.min(a.length, b.length); i += 1) {\n const [x, y] = [a[i]!, b[i]!];\n if (x === y) continue;\n const [nx, ny] = [/^\\d+$/.test(x), /^\\d+$/.test(y)];\n if (nx && ny) return Number(x) < Number(y) ? -1 : 1;\n if (nx !== ny) return nx ? -1 : 1;\n return x < y ? -1 : 1;\n }\n return a.length === b.length ? 0 : a.length < b.length ? -1 : 1;\n}\n\n/**\n * Does `stamp` name an older version than `binary`? A stamp NEWER than the\n * binary does not trail: that is an old binary, not a stale file, and the fix\n * (upgrade loam) is not an AGENTS.md edit.\n *\n * Prerelease identifiers are compared, not ignored, and the difference is the\n * whole reason this check exists. Comparing the numeric triple alone made\n * `0.1.0-beta.1` and `0.1.0-beta.2` indistinguishable — and beta.2 is precisely\n * the release that changed the FORM of every generated file (embedded protocol\n * text became a pointer at `loam instructions`), with a CHANGELOG telling\n * readers to delete the files and re-run `loam init`. The one upgrade that most\n * needed the warning was the one bump shape that could not raise it. A\n * prerelease line is where a generated file's form actually moves, because that\n * is what a prerelease is for.\n */\nexport function versionTrails(stamp: string, binary: string): boolean {\n const s = parseVersion(stamp);\n const b = parseVersion(binary);\n if (s === null || b === null) return false;\n for (let i = 0; i < 3; i += 1) {\n if (s.release[i]! !== b.release[i]!) return s.release[i]! < b.release[i]!;\n }\n return comparePre(s.pre, b.pre) < 0;\n}\n\n/**\n * Does `stamp` name a version NEWER than the binary reading it? The mirror of\n * {@link versionTrails}, and a different subject rather than its negation:\n * equal versions are neither.\n */\nexport function versionAhead(stamp: string, binary: string): boolean {\n return versionTrails(binary, stamp);\n}\n\n/**\n * `docs.binary-behind` — this loam is OLDER than the loam that wrote the docs\n * repo it is reading.\n *\n * The false-green this closes is the sharpest one in the product, because it\n * inverts the meaning of a passing run rather than merely missing a defect.\n * loam's whole promise is that a token spelled here must be spelled there — so\n * every check is an existence constraint over a value the parser recognised.\n * A binary that predates a grammar addition does not FAIL on the newer\n * directive; it does not see it, produces no value, has nothing to join, and\n * reports green. The corpus is checked less than it looks, and nothing anywhere\n * says so. `agents.stale` is the opposite direction and explicitly declines\n * this case (\"that is an old binary, not a stale file\") — correctly, because\n * the fix is different: upgrade loam, do not edit the file.\n *\n * A WARNING, not an error, and the reasoning is the `sources.stale` doctrine.\n * A mixed-version fleet is ordinary — one engineer's global install trails a\n * release by a week — and failing every command in that repo would be a\n * refusal aimed at the wrong person on the wrong day. What the message must do\n * instead is say plainly that a green run from this binary is worth less than\n * it appears, which no other finding says. A fleet that wants it to gate has\n * `--strict`, which is exactly the lever that doctrine reserves for this.\n *\n * Silent when there is no file, no stamp, or the stamp is equal or older —\n * every one of those is somebody else's finding.\n */\nexport function binaryBehindFinding(text: string | null, binary: string): Finding | null {\n if (text === null) return null;\n const stamp = agentsStampVersion(text);\n if (stamp === null || !versionAhead(stamp, binary)) return null;\n return {\n severity: \"warn\",\n code: \"docs.binary-behind\",\n message:\n `This docs repo was written by loam v${stamp}; this binary is v${binary}. A newer loam may check ` +\n \"joins this one cannot parse — an unrecognised directive is read as prose, so it produces no \" +\n \"join to fail and this run reports green without having graded it. Treat a pass from this \" +\n `binary as incomplete rather than clean, and upgrade to v${stamp} or later (\\`npm i -g @ybotok/loam\\`). ` +\n \"Nothing here is wrong with the documents; the reader is behind them.\",\n };\n}\n\n/**\n * The one finding `loam validate --all` owes the agent contract. `text` is the\n * docs repo's AGENTS.md, or null when there is none — no file means no contract\n * to have drifted, and this check stays silent rather than inventing a second\n * meaning for its code. Returns null when the stamp is current (or newer).\n */\nexport function agentsStaleFinding(text: string | null, binary: string): Finding | null {\n if (text === null) return null;\n const stamp = agentsStampVersion(text);\n if (stamp === null) {\n return {\n severity: \"warn\",\n code: \"agents.stale\",\n message:\n \"AGENTS.md has no version stamp — it predates stamping or was hand-created, so nothing says \" +\n \"which loam its flag and code tables describe. Review it against the current `loam --help`, \" +\n `then add \\`${agentsStampLine(binary)}\\` near the top; a hand-curated file keeps the warning ` +\n \"off the same way, by keeping the stamp current.\",\n };\n }\n if (!versionTrails(stamp, binary)) return null;\n return {\n severity: \"warn\",\n code: \"agents.stale\",\n message:\n `AGENTS.md was written by loam v${stamp}; this binary is v${binary} — the flag and code tables ` +\n \"agents branch on may have moved. Review the file against the current `loam --help`, then \" +\n `update the stamp line to v${binary} (a hand-curated file silences this by bumping the stamp itself).`,\n };\n}\n"]}
1
+ {"version":3,"file":"agents-stamp.js","sourceRoot":"","sources":["../../../src/core/agent/agents-stamp.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The AGENTS.md version stamp: detection, never refresh.\n *\n * AGENTS.md is written once by `loam init` and never touched again — that is\n * the ownership contract (agent.ts): it is a starting point, and a team's edits\n * to it outrank ours. But the file tells agents to branch on flag and code\n * tables, and those tables describe the binary that wrote them. OpenSpec hit\n * the same wall and answered with auto-refresh, which teaches everyone the file\n * belongs to the tool after all; loam answers with the smallest honest thing —\n * a version stamp in the generated file, and one warning when the stamp is\n * missing or trails the running binary. Reviewing the file and bumping the\n * stamp is a human act, and a hand-curated file silences the warning the same\n * way: by saying, in the stamp, that someone has looked.\n */\nimport type { Finding } from \"../vocabulary/report.js\";\n\n/** The stamp `loam init` writes at the top of AGENTS.md. */\nexport function agentsStampLine(version: string): string {\n return `<!-- generated by loam v${version} -->`;\n}\n\n/** Matches a stamp line anywhere in the file; the capture is the version text. */\nconst STAMP_RE = /<!--\\s*generated by loam v(\\S+?)\\s*-->/;\n\n/**\n * `text` with its stamp, when it has one, rewritten to name `version` — every\n * other byte untouched. What lets a caller ask whether a generated file differs\n * from what a binary would write in anything BUT the stamp: the pointer files\n * carry this same line, and a release that changes nothing in their body still\n * changes it.\n */\nexport function restamped(text: string, version: string): string {\n return text.replace(STAMP_RE, agentsStampLine(version));\n}\n\n/**\n * The version the stamp names, or null when there is no stamp (or one whose\n * version does not read as numbers — unparseable is indistinguishable from\n * absent, and both get the same advice).\n */\nexport function agentsStampVersion(text: string): string | null {\n const m = STAMP_RE.exec(text);\n return m !== null && parseVersion(m[1]!) !== null ? m[1]! : null;\n}\n\n/** A version split the way precedence is decided: the triple, then the prerelease identifiers. */\ninterface ParsedVersion {\n release: [number, number, number];\n /**\n * The dot-separated identifiers of a `-beta.2` suffix, empty for a final\n * release. Build metadata (`+sha`) is dropped — semver excludes it from\n * precedence, and two builds of one version are the same instructions.\n */\n pre: string[];\n}\n\n/** `X.Y.Z[-pre][+build]`, or null when the leading triple does not read as numbers. */\nfunction parseVersion(v: string): ParsedVersion | null {\n const m = /^(\\d+)\\.(\\d+)\\.(\\d+)(?:-([0-9A-Za-z.-]+))?(?:\\+[0-9A-Za-z.-]+)?$/.exec(v);\n if (m === null) return null;\n return {\n release: [Number(m[1]), Number(m[2]), Number(m[3])],\n pre: m[4] === undefined ? [] : m[4].split(\".\"),\n };\n}\n\n/**\n * Semver identifier precedence: numeric identifiers compare numerically and\n * rank below alphanumeric ones, and a longer identifier list outranks a prefix\n * of itself. -1 / 0 / 1, so the caller reads like the triple comparison above.\n */\nfunction comparePre(a: string[], b: string[]): number {\n // No prerelease outranks any prerelease — `1.2.0` is newer than `1.2.0-beta.9`\n // — which is why this cannot be the plain lexicographic walk the identifiers get.\n if (a.length === 0 || b.length === 0) return a.length === b.length ? 0 : a.length === 0 ? 1 : -1;\n for (let i = 0; i < Math.min(a.length, b.length); i += 1) {\n const [x, y] = [a[i]!, b[i]!];\n if (x === y) continue;\n const [nx, ny] = [/^\\d+$/.test(x), /^\\d+$/.test(y)];\n if (nx && ny) return Number(x) < Number(y) ? -1 : 1;\n if (nx !== ny) return nx ? -1 : 1;\n return x < y ? -1 : 1;\n }\n return a.length === b.length ? 0 : a.length < b.length ? -1 : 1;\n}\n\n/**\n * Does `stamp` name an older version than `binary`? A stamp NEWER than the\n * binary does not trail: that is an old binary, not a stale file, and the fix\n * (upgrade loam) is not an AGENTS.md edit.\n *\n * Prerelease identifiers are compared, not ignored, and the difference is the\n * whole reason this check exists. Comparing the numeric triple alone made\n * `0.1.0-beta.1` and `0.1.0-beta.2` indistinguishable — and beta.2 is precisely\n * the release that changed the FORM of every generated file (embedded protocol\n * text became a pointer at `loam instructions`), with a CHANGELOG telling\n * readers to delete the files and re-run `loam init`. The one upgrade that most\n * needed the warning was the one bump shape that could not raise it. A\n * prerelease line is where a generated file's form actually moves, because that\n * is what a prerelease is for.\n */\nexport function versionTrails(stamp: string, binary: string): boolean {\n const s = parseVersion(stamp);\n const b = parseVersion(binary);\n if (s === null || b === null) return false;\n for (let i = 0; i < 3; i += 1) {\n if (s.release[i]! !== b.release[i]!) return s.release[i]! < b.release[i]!;\n }\n return comparePre(s.pre, b.pre) < 0;\n}\n\n/**\n * Does `stamp` name a version NEWER than the binary reading it? The mirror of\n * {@link versionTrails}, and a different subject rather than its negation:\n * equal versions are neither.\n */\nexport function versionAhead(stamp: string, binary: string): boolean {\n return versionTrails(binary, stamp);\n}\n\n/**\n * `docs.binary-behind` — this loam is OLDER than the loam that wrote the docs\n * repo it is reading.\n *\n * The false-green this closes is the sharpest one in the product, because it\n * inverts the meaning of a passing run rather than merely missing a defect.\n * loam's whole promise is that a token spelled here must be spelled there — so\n * every check is an existence constraint over a value the parser recognised.\n * A binary that predates a grammar addition does not FAIL on the newer\n * directive; it does not see it, produces no value, has nothing to join, and\n * reports green. The corpus is checked less than it looks, and nothing anywhere\n * says so. `agents.stale` is the opposite direction and explicitly declines\n * this case (\"that is an old binary, not a stale file\") — correctly, because\n * the fix is different: upgrade loam, do not edit the file.\n *\n * A WARNING, not an error, and the reasoning is the `sources.stale` doctrine.\n * A mixed-version fleet is ordinary — one engineer's global install trails a\n * release by a week — and failing every command in that repo would be a\n * refusal aimed at the wrong person on the wrong day. What the message must do\n * instead is say plainly that a green run from this binary is worth less than\n * it appears, which no other finding says. A fleet that wants it to gate has\n * `--strict`, which is exactly the lever that doctrine reserves for this.\n *\n * Silent when there is no file, no stamp, or the stamp is equal or older —\n * every one of those is somebody else's finding.\n */\nexport function binaryBehindFinding(text: string | null, binary: string): Finding | null {\n if (text === null) return null;\n const stamp = agentsStampVersion(text);\n if (stamp === null || !versionAhead(stamp, binary)) return null;\n return {\n severity: \"warn\",\n code: \"docs.binary-behind\",\n message:\n `This docs repo was written by loam v${stamp}; this binary is v${binary}. A newer loam may check ` +\n \"joins this one cannot parse — an unrecognised directive is read as prose, so it produces no \" +\n \"join to fail and this run reports green without having graded it. Treat a pass from this \" +\n `binary as incomplete rather than clean, and upgrade to v${stamp} or later (\\`npm i -g @ybotok/loam\\`). ` +\n \"Nothing here is wrong with the documents; the reader is behind them.\",\n };\n}\n\n/**\n * The one finding `loam validate --all` owes the agent contract. `text` is the\n * docs repo's AGENTS.md, or null when there is none — no file means no contract\n * to have drifted, and this check stays silent rather than inventing a second\n * meaning for its code. Returns null when the stamp is current (or newer).\n */\nexport function agentsStaleFinding(text: string | null, binary: string): Finding | null {\n if (text === null) return null;\n const stamp = agentsStampVersion(text);\n if (stamp === null) {\n return {\n severity: \"warn\",\n code: \"agents.stale\",\n message:\n \"AGENTS.md has no version stamp — it predates stamping or was hand-created, so nothing says \" +\n \"which loam its flag and code tables describe. Review it against the current `loam --help`, \" +\n `then add \\`${agentsStampLine(binary)}\\` near the top; a hand-curated file keeps the warning ` +\n \"off the same way, by keeping the stamp current.\",\n };\n }\n if (!versionTrails(stamp, binary)) return null;\n return {\n severity: \"warn\",\n code: \"agents.stale\",\n message:\n `AGENTS.md was written by loam v${stamp}; this binary is v${binary} — the flag and code tables ` +\n \"agents branch on may have moved. Review the file against the current `loam --help`, then \" +\n `update the stamp line to v${binary} (a hand-curated file silences this by bumping the stamp itself).`,\n };\n}\n"]}
@@ -53,8 +53,9 @@ import { AGENT_TOOLS, CLAUDE } from "./tools/registry.js";
53
53
  * Thinning the file is the third option. What remains is what does not move
54
54
  * between releases: what this workflow is for, and the verbs in order. What
55
55
  * leaves is everything version-shaped. `doctor.agent-files-stale` still reports
56
- * a file whose stamp has fallen behind, and now has almost nothing to be right
57
- * about — which is the point.
56
+ * a file whose stamp has fallen behind — once its body, stamp aside, is no
57
+ * longer the one this binary writes — and now has almost nothing to be right
58
+ * about, which is the point.
58
59
  *
59
60
  * The trade is real and worth stating: an agent that cannot execute `loam` now
60
61
  * has a spine instead of a protocol. That is the correct failure. The exact
@@ -64,12 +65,13 @@ import { AGENT_TOOLS, CLAUDE } from "./tools/registry.js";
64
65
  *
65
66
  * The last clause of the pointer is the one sentence here that changes what an
66
67
  * agent DOES rather than what it knows. `loam instructions loam-check` prints
67
- * 84 KB — about a fifth of a 100k-token window, 223 fix-table rows of which a
68
- * run needs two or three — and every file written from this stub opens by
69
- * telling the agent to run it. `--no-fix-tables` (commands/instructions.ts)
70
- * drops the rows, and `loam explain <code>` answers the one row the run turned
71
- * out to need in under 500 bytes. The flag is what makes the lazy path payable;
72
- * this sentence is what makes an agent take it.
68
+ * the largest page loam has — 109 KB measured 2026-10-10 (84 KB when this was
69
+ * first written), about a quarter of a 100k-token window, and hundreds of
70
+ * fix-table rows of which a run needs two or three — and every file written
71
+ * from this stub opens by telling the agent to run it. `--no-fix-tables`
72
+ * (commands/instructions.ts) drops the rows, and `loam explain <code>` answers
73
+ * the one row the run turned out to need on its own. The flag is what makes
74
+ * the lazy path payable; this sentence is what makes an agent take it.
73
75
  */
74
76
  function stubBody(c) {
75
77
  const steps = c.spine.map((s, i) => ` ${i + 1}. ${s}`).join("\n");
@@ -234,13 +236,6 @@ export async function syncAgentCommands(req) {
234
236
  }
235
237
  return out;
236
238
  }
237
- /**
238
- * Backward-compatible create-only wrapper. Callers that own no digest manifest
239
- * leave every existing file alone and receive only the paths created.
240
- */
241
- export async function scaffoldAgentCommands(cwd, toolIds = ["claude"], delivery = DELIVERIES) {
242
- return (await syncAgentCommands({ cwd, toolIds, delivery })).created;
243
- }
244
239
  /**
245
240
  * The registry ids whose marker paths are present in `cwd` — what `loam init`
246
241
  * writes for when nobody passed `--tools`.
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../../src/core/agent/scaffold.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The generated FILES: what a pointer file contains (the stub), what this\n * binary would write (SLASH_COMMANDS, plannedCommandFiles), and how the files\n * land and are detected on disk.\n *\n * The command and skill files go into the repo `init` runs in, because that is\n * where the agent is invoked — for whichever tools the repo shows signs of, via\n * the AGENT_TOOLS registry (tools/registry.ts): one shared body, a per-tool\n * path and wrapper. A delivery is refreshed only while its bytes still match\n * the digest loam recorded when it wrote them; a team's edits revoke that\n * authority and outrank ours.\n *\n * Two deliveries, one body. A slash command has to be TYPED, so it only ever\n * reaches an agent whose operator already knows loam exists. A skill is loaded\n * by the model itself when the task matches its `description`, which is how the\n * protocol reaches an agent that was never told about it. That difference is\n * the whole reason both are written by default.\n *\n * The per-tool command and skill files carry the same version stamp AGENTS.md\n * does (agents-md.ts), for the same reason and with the same answer:\n * `loam doctor` reports `doctor.agent-files-stale` without writing. A later\n * `loam init` can refresh only a digest-matched pointer. Without the stamp,\n * absence was the only drift doctor could see — a command file mangled to one\n * line left it saying `healthy: true`, which across a hundred repositories is\n * invisible rot with no repair path.\n */\nimport { createHash } from \"node:crypto\";\nimport { existsSync } from \"node:fs\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join, relative } from \"node:path\";\nimport { agentsStampLine } from \"./agents-stamp.js\";\nimport { LOAM_VERSION } from \"../envelope/version.js\";\nimport type { AgentFileEmitter, CommandContent } from \"./contract.js\";\nimport { AGENT_COMMANDS } from \"./protocol.js\";\nimport { AGENT_TOOLS, CLAUDE } from \"./tools/registry.js\";\n\n/**\n * What a generated file actually contains: the purpose, the spine, and the\n * command that prints the rest.\n *\n * The protocol itself stays in `body` and ships inside the binary, reachable as\n * `loam instructions <name>`. The file on disk gets a pointer to it, because\n * these two things go stale in opposite directions and only one of them can be\n * fixed. A generated file is refreshed only while its bytes still match the\n * digest loam recorded; any edit revokes that authority because your edits\n * outrank the template. Without that distinction a repo scaffolded a year ago\n * holds a year-old protocol: exact\n * flags, exact finding codes, exact step order, all of it asserted with total\n * confidence by a file whose reader has no way to know it is describing a\n * different release. That is not a hypothetical failure mode. It is the one\n * every project shipping generated agent instructions has had, and the fix\n * everybody reaches for — regenerate on upgrade — trades it for silently\n * overwriting what a human wrote.\n *\n * Thinning the file is the third option. What remains is what does not move\n * between releases: what this workflow is for, and the verbs in order. What\n * leaves is everything version-shaped. `doctor.agent-files-stale` still reports\n * a file whose stamp has fallen behind, and now has almost nothing to be right\n * about — which is the point.\n *\n * The trade is real and worth stating: an agent that cannot execute `loam` now\n * has a spine instead of a protocol. That is the correct failure. The exact\n * diagnostic and lifecycle invocations belong to the binary-owned page; an\n * agent that cannot run loam cannot finish either kind from an embedded copy —\n * it can only try using a year-old flag.\n *\n * The last clause of the pointer is the one sentence here that changes what an\n * agent DOES rather than what it knows. `loam instructions loam-check` prints\n * 84 KB — about a fifth of a 100k-token window, 223 fix-table rows of which a\n * run needs two or three — and every file written from this stub opens by\n * telling the agent to run it. `--no-fix-tables` (commands/instructions.ts)\n * drops the rows, and `loam explain <code>` answers the one row the run turned\n * out to need in under 500 bytes. The flag is what makes the lazy path payable;\n * this sentence is what makes an agent take it.\n */\nfunction stubBody(c: CommandContent): string {\n const steps = c.spine.map((s, i) => ` ${i + 1}. ${s}`).join(\"\\n\");\n const entry = c.name === \"loam-report\"\n ? `This support protocol has two equivalent chat entry points: the explicit command a\nuser chooses, and the Agent Skill an unexpected-run request may load. Both reach\nthe same local, sanitized report; the agent owns the diagnostic steps.`\n : `This workflow has two equivalent chat entry points: the explicit command a user\nchooses, and the Agent Skill a natural-language request may load. Both reach this\nsame body and must produce the same status/edit/validate loop; do not make the\nuser type the internal loam commands one by one.`;\n const output = c.name === \"loam-report\"\n ? `The runtime protocol decides which diagnostics are safe to run and what may enter the\nreport. Do not infer permission to retry a writer, repair files or send the result\nfrom the broader lifecycle commands another generated skill may allow.`\n : `Every step above is a \\`loam\\` invocation, and each command's own \\`--json\\` output\ncarries what to do next: findings have stable codes, and \\`loam status --json\\` puts\nthe ordered \\`next[]\\` — each entry a code and the literal command — in one place.\nBranch on the codes, never on the prose. The check workflow starts with\n\\`--no-fix-tables\\`; use \\`loam explain <code>\\` for each code the run actually\nreports, and remove that flag only when you deliberately need the complete table.`;\n return `${c.purpose}\n\n${entry}\n\n**Run this first.** It is the protocol, and it ships with the binary you are about\nto call — so it names this loam's flags, its finding codes and its fix tables,\nnot the ones that were current when this repository was scaffolded:\n\n ${c.invocation}\n\nThe spine it fills in, so you can tell a run that went sideways from one that did not:\n\n${steps}\n\n${output}\n\nThis file is a pointer, not the protocol. loam may refresh it while its bytes still\nmatch the digest recorded in loam.json. Editing it revokes that authority: your\nchanges outrank the template and stay untouched. Where this file and\n\\`loam instructions\\` disagree, the command is right.\n`;\n}\n\n/**\n * The command body a file actually gets: the pointer, under the same version\n * stamp AGENTS.md carries.\n *\n * The stamp is applied once, here, rather than in twenty wrappers — it is a\n * fact about loam, not about a tool's dialect, and a per-wrapper copy is twenty\n * chances to forget it. It rides at the top of the BODY (not the file) because\n * every dialect puts something of its own first: YAML frontmatter, a TOML key,\n * a title line. An HTML comment is invisible in all of them, and\n * `agentsStampVersion` matches it wherever in the file it lands.\n *\n * A repo initialized before stamping existed has unstamped files, which is\n * exactly what `doctor.agent-files-stale` is for: nobody has confirmed that\n * those instructions still describe this binary.\n */\nconst stubbed = (c: CommandContent): CommandContent => ({\n ...c,\n body: `${agentsStampLine(LOAM_VERSION)}\\n\\n${stubBody(c)}`,\n});\n\n/**\n * The Claude-format files, keyed by command name: exactly what **this** binary\n * would write, derived through the same adapter that writes them, so the export\n * and a freshly scaffolded repo can never disagree.\n *\n * Read that scope literally. It used to be described as the on-disk contract of\n * every repo initialized before `--tools` existed, and that is no longer true\n * of anything but a fresh scaffold: a file holds the STUB now, every repo\n * scaffolded before that holds the full protocol. A recorded, byte-identical\n * pointer can refresh; an older unrecorded file or any customized file cannot,\n * so an export cannot describe files this binary did not write.\n *\n * The protocol those older files carry is `PROTOCOLS`. Anything asserting on\n * protocol content wants that one — this answers \"what would loam write here\",\n * not \"what does loam instruct\".\n */\nexport const SLASH_COMMANDS: Record<string, string> = Object.fromEntries(\n AGENT_COMMANDS.map((c) => [c.name, CLAUDE.format(stubbed(c))]),\n);\n\n/** The two ways a command body reaches an agent. Both are on by default. */\nexport const DELIVERIES = [\"commands\", \"skills\"] as const;\nexport type Delivery = (typeof DELIVERIES)[number];\n\nexport const AGENT_PROFILES = [\"full\", \"service\", \"docs\"] as const;\nexport type AgentProfile = (typeof AGENT_PROFILES)[number];\n\nconst PROFILE_COMMANDS: Record<AgentProfile, ReadonlySet<string>> = {\n full: new Set(AGENT_COMMANDS.map((command) => command.name)),\n service: new Set([\"loam-adopt\", \"loam-implement\", \"loam-check\", \"loam-verify\", \"loam-report\"]),\n docs: new Set([\"loam-feature\", \"loam-check\", \"loam-ship\", \"loam-report\"]),\n};\n\n/**\n * Every file the selected tools would lay down, in creation order — the same\n * list `init` probes for `skipped` BEFORE the scaffold runs, so created +\n * skipped is the same list on every repo.\n *\n * Called with two arguments it returns EVERYTHING the binary would write under\n * default delivery: commands and skills both. That is the contract callers\n * outside this module depend on — `loam doctor` asks \"what should be here?\"\n * and a half-answer would report a fully-scaffolded repo as fully scaffolded\n * while ignoring every skill file. The name predates the second delivery and is\n * kept because it is imported elsewhere; `delivery` narrows it.\n */\nexport function plannedCommandFiles(\n cwd: string,\n toolIds: string[],\n delivery: readonly Delivery[] = DELIVERIES,\n profile: AgentProfile = \"full\",\n): Array<{ path: string; content: string }> {\n return toolIds.flatMap((id) => {\n const tool = AGENT_TOOLS[id];\n if (tool === undefined) throw new Error(`unknown agent tool: ${id}`);\n const emitters: AgentFileEmitter[] = [];\n // A tool declares only the deliveries it actually reads, so an absent\n // emitter is a fact about the tool, never a reason to fall back to some\n // other tool's convention.\n const { path, format, skill } = tool;\n if (delivery.includes(\"commands\") && path !== undefined && format !== undefined) {\n emitters.push({ path, format });\n }\n if (delivery.includes(\"skills\") && skill !== undefined) emitters.push(skill);\n return emitters.flatMap((e) =>\n AGENT_COMMANDS.filter((command) => PROFILE_COMMANDS[profile].has(command.name)).map((c) => ({\n path: join(cwd, ...e.path(c.name)),\n content: e.format(stubbed(c)),\n })),\n );\n });\n}\n\nfunction contentDigest(content: string | Buffer): string {\n return createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\nfunction manifestPath(cwd: string, path: string): string {\n return relative(cwd, path).replaceAll(\"\\\\\", \"/\");\n}\n\nexport function updatedAgentFileManifest(\n cwd: string,\n planned: Array<{ path: string }>,\n existing: Readonly<Record<string, string>> | undefined,\n managed: Readonly<Record<string, string>>,\n): Record<string, string> {\n const out = { ...existing };\n for (const file of planned) delete out[manifestPath(cwd, file.path)];\n return Object.assign(out, managed);\n}\n\nexport interface AgentFileSync {\n created: string[];\n refreshed: string[];\n /** Repo-relative path -> digest of the bytes loam is allowed to refresh. */\n managed: Record<string, string>;\n}\n\n/**\n * Create missing files and refresh only files still byte-identical to the\n * digest recorded when loam last wrote them. A human edit breaks that equality\n * and therefore revokes loam's authority over the file without a marker block\n * or a prompt.\n */\nexport async function syncAgentCommands(req: {\n cwd: string;\n toolIds?: string[];\n delivery?: readonly Delivery[];\n profile?: AgentProfile;\n known?: Readonly<Record<string, string>>;\n}): Promise<AgentFileSync> {\n const { cwd, toolIds = [\"claude\"], delivery = DELIVERIES, profile = \"full\", known = {} } = req;\n const out: AgentFileSync = { created: [], refreshed: [], managed: {} };\n for (const { path, content } of plannedCommandFiles(cwd, toolIds, delivery, profile)) {\n const key = manifestPath(cwd, path);\n const desired = contentDigest(content);\n if (!existsSync(path)) {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, content, \"utf8\");\n out.created.push(path);\n out.managed[key] = desired;\n continue;\n }\n const current = contentDigest(await readFile(path));\n if (current === desired) {\n out.managed[key] = desired;\n continue;\n }\n if (known[key] !== current) continue;\n await writeFile(path, content, \"utf8\");\n out.refreshed.push(path);\n out.managed[key] = desired;\n }\n return out;\n}\n\n/**\n * Backward-compatible create-only wrapper. Callers that own no digest manifest\n * leave every existing file alone and receive only the paths created.\n */\nexport async function scaffoldAgentCommands(\n cwd: string,\n toolIds: string[] = [\"claude\"],\n delivery: readonly Delivery[] = DELIVERIES,\n): Promise<string[]> {\n return (await syncAgentCommands({ cwd, toolIds, delivery })).created;\n}\n\n/**\n * The registry ids whose marker paths are present in `cwd` — what `loam init`\n * writes for when nobody passed `--tools`.\n *\n * A repo with `.cursor/` and no Claude Code used to get `.claude/commands/`\n * and nothing it could actually run. Presence is the whole test, and existence\n * (not directory-ness) is what is asked, because a marker is as often a file as\n * a directory — `.github/copilot-instructions.md` is the honest signal for\n * Copilot where `.github/` itself is noise.\n */\nexport function detectAgentTools(cwd: string): string[] {\n return Object.entries(AGENT_TOOLS)\n .filter(([, tool]) => tool.detect.some((marker) => existsSync(join(cwd, marker))))\n .map(([id]) => id);\n}\n"]}
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../../src/core/agent/scaffold.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The generated FILES: what a pointer file contains (the stub), what this\n * binary would write (SLASH_COMMANDS, plannedCommandFiles), and how the files\n * land and are detected on disk.\n *\n * The command and skill files go into the repo `init` runs in, because that is\n * where the agent is invoked — for whichever tools the repo shows signs of, via\n * the AGENT_TOOLS registry (tools/registry.ts): one shared body, a per-tool\n * path and wrapper. A delivery is refreshed only while its bytes still match\n * the digest loam recorded when it wrote them; a team's edits revoke that\n * authority and outrank ours.\n *\n * Two deliveries, one body. A slash command has to be TYPED, so it only ever\n * reaches an agent whose operator already knows loam exists. A skill is loaded\n * by the model itself when the task matches its `description`, which is how the\n * protocol reaches an agent that was never told about it. That difference is\n * the whole reason both are written by default.\n *\n * The per-tool command and skill files carry the same version stamp AGENTS.md\n * does (agents-md.ts), for the same reason and with the same answer:\n * `loam doctor` reports `doctor.agent-files-stale` without writing. A later\n * `loam init` can refresh only a digest-matched pointer. Without the stamp,\n * absence was the only drift doctor could see — a command file mangled to one\n * line left it saying `healthy: true`, which across a hundred repositories is\n * invisible rot with no repair path.\n */\nimport { createHash } from \"node:crypto\";\nimport { existsSync } from \"node:fs\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join, relative } from \"node:path\";\nimport { agentsStampLine } from \"./agents-stamp.js\";\nimport { LOAM_VERSION } from \"../envelope/version.js\";\nimport type { AgentFileEmitter, CommandContent } from \"./contract.js\";\nimport { AGENT_COMMANDS } from \"./protocol.js\";\nimport { AGENT_TOOLS, CLAUDE } from \"./tools/registry.js\";\n\n/**\n * What a generated file actually contains: the purpose, the spine, and the\n * command that prints the rest.\n *\n * The protocol itself stays in `body` and ships inside the binary, reachable as\n * `loam instructions <name>`. The file on disk gets a pointer to it, because\n * these two things go stale in opposite directions and only one of them can be\n * fixed. A generated file is refreshed only while its bytes still match the\n * digest loam recorded; any edit revokes that authority because your edits\n * outrank the template. Without that distinction a repo scaffolded a year ago\n * holds a year-old protocol: exact\n * flags, exact finding codes, exact step order, all of it asserted with total\n * confidence by a file whose reader has no way to know it is describing a\n * different release. That is not a hypothetical failure mode. It is the one\n * every project shipping generated agent instructions has had, and the fix\n * everybody reaches for — regenerate on upgrade — trades it for silently\n * overwriting what a human wrote.\n *\n * Thinning the file is the third option. What remains is what does not move\n * between releases: what this workflow is for, and the verbs in order. What\n * leaves is everything version-shaped. `doctor.agent-files-stale` still reports\n * a file whose stamp has fallen behind — once its body, stamp aside, is no\n * longer the one this binary writes — and now has almost nothing to be right\n * about, which is the point.\n *\n * The trade is real and worth stating: an agent that cannot execute `loam` now\n * has a spine instead of a protocol. That is the correct failure. The exact\n * diagnostic and lifecycle invocations belong to the binary-owned page; an\n * agent that cannot run loam cannot finish either kind from an embedded copy —\n * it can only try using a year-old flag.\n *\n * The last clause of the pointer is the one sentence here that changes what an\n * agent DOES rather than what it knows. `loam instructions loam-check` prints\n * the largest page loam has — 109 KB measured 2026-10-10 (84 KB when this was\n * first written), about a quarter of a 100k-token window, and hundreds of\n * fix-table rows of which a run needs two or three — and every file written\n * from this stub opens by telling the agent to run it. `--no-fix-tables`\n * (commands/instructions.ts) drops the rows, and `loam explain <code>` answers\n * the one row the run turned out to need on its own. The flag is what makes\n * the lazy path payable; this sentence is what makes an agent take it.\n */\nfunction stubBody(c: CommandContent): string {\n const steps = c.spine.map((s, i) => ` ${i + 1}. ${s}`).join(\"\\n\");\n const entry = c.name === \"loam-report\"\n ? `This support protocol has two equivalent chat entry points: the explicit command a\nuser chooses, and the Agent Skill an unexpected-run request may load. Both reach\nthe same local, sanitized report; the agent owns the diagnostic steps.`\n : `This workflow has two equivalent chat entry points: the explicit command a user\nchooses, and the Agent Skill a natural-language request may load. Both reach this\nsame body and must produce the same status/edit/validate loop; do not make the\nuser type the internal loam commands one by one.`;\n const output = c.name === \"loam-report\"\n ? `The runtime protocol decides which diagnostics are safe to run and what may enter the\nreport. Do not infer permission to retry a writer, repair files or send the result\nfrom the broader lifecycle commands another generated skill may allow.`\n : `Every step above is a \\`loam\\` invocation, and each command's own \\`--json\\` output\ncarries what to do next: findings have stable codes, and \\`loam status --json\\` puts\nthe ordered \\`next[]\\` — each entry a code and the literal command — in one place.\nBranch on the codes, never on the prose. The check workflow starts with\n\\`--no-fix-tables\\`; use \\`loam explain <code>\\` for each code the run actually\nreports, and remove that flag only when you deliberately need the complete table.`;\n return `${c.purpose}\n\n${entry}\n\n**Run this first.** It is the protocol, and it ships with the binary you are about\nto call — so it names this loam's flags, its finding codes and its fix tables,\nnot the ones that were current when this repository was scaffolded:\n\n ${c.invocation}\n\nThe spine it fills in, so you can tell a run that went sideways from one that did not:\n\n${steps}\n\n${output}\n\nThis file is a pointer, not the protocol. loam may refresh it while its bytes still\nmatch the digest recorded in loam.json. Editing it revokes that authority: your\nchanges outrank the template and stay untouched. Where this file and\n\\`loam instructions\\` disagree, the command is right.\n`;\n}\n\n/**\n * The command body a file actually gets: the pointer, under the same version\n * stamp AGENTS.md carries.\n *\n * The stamp is applied once, here, rather than in twenty wrappers — it is a\n * fact about loam, not about a tool's dialect, and a per-wrapper copy is twenty\n * chances to forget it. It rides at the top of the BODY (not the file) because\n * every dialect puts something of its own first: YAML frontmatter, a TOML key,\n * a title line. An HTML comment is invisible in all of them, and\n * `agentsStampVersion` matches it wherever in the file it lands.\n *\n * A repo initialized before stamping existed has unstamped files, which is\n * exactly what `doctor.agent-files-stale` is for: nobody has confirmed that\n * those instructions still describe this binary.\n */\nconst stubbed = (c: CommandContent): CommandContent => ({\n ...c,\n body: `${agentsStampLine(LOAM_VERSION)}\\n\\n${stubBody(c)}`,\n});\n\n/**\n * The Claude-format files, keyed by command name: exactly what **this** binary\n * would write, derived through the same adapter that writes them, so the export\n * and a freshly scaffolded repo can never disagree.\n *\n * Read that scope literally. It used to be described as the on-disk contract of\n * every repo initialized before `--tools` existed, and that is no longer true\n * of anything but a fresh scaffold: a file holds the STUB now, every repo\n * scaffolded before that holds the full protocol. A recorded, byte-identical\n * pointer can refresh; an older unrecorded file or any customized file cannot,\n * so an export cannot describe files this binary did not write.\n *\n * The protocol those older files carry is `PROTOCOLS`. Anything asserting on\n * protocol content wants that one — this answers \"what would loam write here\",\n * not \"what does loam instruct\".\n */\nexport const SLASH_COMMANDS: Record<string, string> = Object.fromEntries(\n AGENT_COMMANDS.map((c) => [c.name, CLAUDE.format(stubbed(c))]),\n);\n\n/** The two ways a command body reaches an agent. Both are on by default. */\nexport const DELIVERIES = [\"commands\", \"skills\"] as const;\nexport type Delivery = (typeof DELIVERIES)[number];\n\nexport const AGENT_PROFILES = [\"full\", \"service\", \"docs\"] as const;\nexport type AgentProfile = (typeof AGENT_PROFILES)[number];\n\nconst PROFILE_COMMANDS: Record<AgentProfile, ReadonlySet<string>> = {\n full: new Set(AGENT_COMMANDS.map((command) => command.name)),\n service: new Set([\"loam-adopt\", \"loam-implement\", \"loam-check\", \"loam-verify\", \"loam-report\"]),\n docs: new Set([\"loam-feature\", \"loam-check\", \"loam-ship\", \"loam-report\"]),\n};\n\n/**\n * Every file the selected tools would lay down, in creation order — the same\n * list `init` probes for `skipped` BEFORE the scaffold runs, so created +\n * skipped is the same list on every repo.\n *\n * Called with two arguments it returns EVERYTHING the binary would write under\n * default delivery: commands and skills both. That is the contract callers\n * outside this module depend on — `loam doctor` asks \"what should be here?\"\n * and a half-answer would report a fully-scaffolded repo as fully scaffolded\n * while ignoring every skill file. The name predates the second delivery and is\n * kept because it is imported elsewhere; `delivery` narrows it.\n */\nexport function plannedCommandFiles(\n cwd: string,\n toolIds: string[],\n delivery: readonly Delivery[] = DELIVERIES,\n profile: AgentProfile = \"full\",\n): Array<{ path: string; content: string }> {\n return toolIds.flatMap((id) => {\n const tool = AGENT_TOOLS[id];\n if (tool === undefined) throw new Error(`unknown agent tool: ${id}`);\n const emitters: AgentFileEmitter[] = [];\n // A tool declares only the deliveries it actually reads, so an absent\n // emitter is a fact about the tool, never a reason to fall back to some\n // other tool's convention.\n const { path, format, skill } = tool;\n if (delivery.includes(\"commands\") && path !== undefined && format !== undefined) {\n emitters.push({ path, format });\n }\n if (delivery.includes(\"skills\") && skill !== undefined) emitters.push(skill);\n return emitters.flatMap((e) =>\n AGENT_COMMANDS.filter((command) => PROFILE_COMMANDS[profile].has(command.name)).map((c) => ({\n path: join(cwd, ...e.path(c.name)),\n content: e.format(stubbed(c)),\n })),\n );\n });\n}\n\nfunction contentDigest(content: string | Buffer): string {\n return createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\nfunction manifestPath(cwd: string, path: string): string {\n return relative(cwd, path).replaceAll(\"\\\\\", \"/\");\n}\n\nexport function updatedAgentFileManifest(\n cwd: string,\n planned: Array<{ path: string }>,\n existing: Readonly<Record<string, string>> | undefined,\n managed: Readonly<Record<string, string>>,\n): Record<string, string> {\n const out = { ...existing };\n for (const file of planned) delete out[manifestPath(cwd, file.path)];\n return Object.assign(out, managed);\n}\n\nexport interface AgentFileSync {\n created: string[];\n refreshed: string[];\n /** Repo-relative path -> digest of the bytes loam is allowed to refresh. */\n managed: Record<string, string>;\n}\n\n/**\n * Create missing files and refresh only files still byte-identical to the\n * digest recorded when loam last wrote them. A human edit breaks that equality\n * and therefore revokes loam's authority over the file without a marker block\n * or a prompt.\n */\nexport async function syncAgentCommands(req: {\n cwd: string;\n toolIds?: string[];\n delivery?: readonly Delivery[];\n profile?: AgentProfile;\n known?: Readonly<Record<string, string>>;\n}): Promise<AgentFileSync> {\n const { cwd, toolIds = [\"claude\"], delivery = DELIVERIES, profile = \"full\", known = {} } = req;\n const out: AgentFileSync = { created: [], refreshed: [], managed: {} };\n for (const { path, content } of plannedCommandFiles(cwd, toolIds, delivery, profile)) {\n const key = manifestPath(cwd, path);\n const desired = contentDigest(content);\n if (!existsSync(path)) {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, content, \"utf8\");\n out.created.push(path);\n out.managed[key] = desired;\n continue;\n }\n const current = contentDigest(await readFile(path));\n if (current === desired) {\n out.managed[key] = desired;\n continue;\n }\n if (known[key] !== current) continue;\n await writeFile(path, content, \"utf8\");\n out.refreshed.push(path);\n out.managed[key] = desired;\n }\n return out;\n}\n\n/**\n * The registry ids whose marker paths are present in `cwd` — what `loam init`\n * writes for when nobody passed `--tools`.\n *\n * A repo with `.cursor/` and no Claude Code used to get `.claude/commands/`\n * and nothing it could actually run. Presence is the whole test, and existence\n * (not directory-ness) is what is asked, because a marker is as often a file as\n * a directory — `.github/copilot-instructions.md` is the honest signal for\n * Copilot where `.github/` itself is noise.\n */\nexport function detectAgentTools(cwd: string): string[] {\n return Object.entries(AGENT_TOOLS)\n .filter(([, tool]) => tool.detect.some((marker) => existsSync(join(cwd, marker))))\n .map(([id]) => id);\n}\n"]}
@@ -58,6 +58,7 @@ const LOAM_ALLOWED_VERBS = [
58
58
  "status",
59
59
  "steps",
60
60
  "subsystem",
61
+ "tasks",
61
62
  "unarchive",
62
63
  "validate",
63
64
  "verify",