@ybotok/loam 0.1.0-beta.3 → 0.2.0-alpha.2

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 (1036) hide show
  1. package/CHANGELOG.md +2635 -86
  2. package/COMPARISON.md +80 -63
  3. package/CONTRIBUTING.md +84 -0
  4. package/MIGRATING-from-OpenSpec.md +213 -54
  5. package/QUICKSTART.md +125 -0
  6. package/README.md +450 -150
  7. package/ROADMAP.md +599 -0
  8. package/SCHEMA.md +2355 -256
  9. package/SECURITY.md +21 -3
  10. package/WORKFLOW.md +701 -0
  11. package/dist/cli.js +113 -19
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/adopt/adopt.js +209 -0
  14. package/dist/commands/adopt/adopt.js.map +1 -0
  15. package/dist/commands/adopt/render.js +268 -0
  16. package/dist/commands/adopt/render.js.map +1 -0
  17. package/dist/commands/archive/archive.js +77 -0
  18. package/dist/commands/archive/archive.js.map +1 -0
  19. package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
  21. package/dist/commands/archive/plan/contracts/openapi.js +159 -0
  22. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
  23. package/dist/commands/archive/plan/gate.js +323 -0
  24. package/dist/commands/archive/plan/gate.js.map +1 -0
  25. package/dist/commands/archive/plan/landscape.js +180 -0
  26. package/dist/commands/archive/plan/landscape.js.map +1 -0
  27. package/dist/commands/archive/plan/refusal.js +111 -0
  28. package/dist/commands/archive/plan/refusal.js.map +1 -0
  29. package/dist/commands/archive/plan/specs.js +237 -0
  30. package/dist/commands/archive/plan/specs.js.map +1 -0
  31. package/dist/commands/archive/plan/state.js +27 -0
  32. package/dist/commands/archive/plan/state.js.map +1 -0
  33. package/dist/commands/archive/run.js +252 -0
  34. package/dist/commands/archive/run.js.map +1 -0
  35. package/dist/commands/context/context.js +99 -0
  36. package/dist/commands/context/context.js.map +1 -0
  37. package/dist/commands/context/print.js +283 -0
  38. package/dist/commands/context/print.js.map +1 -0
  39. package/dist/commands/delta/delta.js +279 -0
  40. package/dist/commands/delta/delta.js.map +1 -0
  41. package/dist/commands/delta/print.js +205 -0
  42. package/dist/commands/delta/print.js.map +1 -0
  43. package/dist/commands/dependencies.js +9 -7
  44. package/dist/commands/dependencies.js.map +1 -1
  45. package/dist/commands/diff/diff.js +93 -0
  46. package/dist/commands/diff/diff.js.map +1 -0
  47. package/dist/commands/diff/print.js +38 -0
  48. package/dist/commands/diff/print.js.map +1 -0
  49. package/dist/commands/doctor.js +5 -4
  50. package/dist/commands/doctor.js.map +1 -1
  51. package/dist/commands/explain/explain.js +151 -0
  52. package/dist/commands/explain/explain.js.map +1 -0
  53. package/dist/commands/explore.js +29 -24
  54. package/dist/commands/explore.js.map +1 -1
  55. package/dist/commands/gate/gate.js +115 -0
  56. package/dist/commands/gate/gate.js.map +1 -0
  57. package/dist/commands/gate/print.js +54 -0
  58. package/dist/commands/gate/print.js.map +1 -0
  59. package/dist/commands/gherkin/commit.js +146 -0
  60. package/dist/commands/gherkin/commit.js.map +1 -0
  61. package/dist/commands/gherkin/gherkin.js +246 -0
  62. package/dist/commands/gherkin/gherkin.js.map +1 -0
  63. package/dist/commands/gherkin/reconcile.js +149 -0
  64. package/dist/commands/gherkin/reconcile.js.map +1 -0
  65. package/dist/commands/gherkin/render.js +77 -0
  66. package/dist/commands/gherkin/render.js.map +1 -0
  67. package/dist/commands/init/example.js +204 -0
  68. package/dist/commands/init/example.js.map +1 -0
  69. package/dist/commands/init/first-hour.js +28 -0
  70. package/dist/commands/init/first-hour.js.map +1 -0
  71. package/dist/commands/{init.js → init/init.js} +146 -71
  72. package/dist/commands/init/init.js.map +1 -0
  73. package/dist/commands/init/mcp.js +131 -0
  74. package/dist/commands/init/mcp.js.map +1 -0
  75. package/dist/commands/init/options.js +78 -0
  76. package/dist/commands/init/options.js.map +1 -0
  77. package/dist/commands/instructions.js +72 -14
  78. package/dist/commands/instructions.js.map +1 -1
  79. package/dist/commands/list/campaign/campaign.js +65 -0
  80. package/dist/commands/list/campaign/campaign.js.map +1 -0
  81. package/dist/commands/list/campaign/owners.js +65 -0
  82. package/dist/commands/list/campaign/owners.js.map +1 -0
  83. package/dist/commands/list/json.js +157 -0
  84. package/dist/commands/list/json.js.map +1 -0
  85. package/dist/commands/list/list.js +343 -0
  86. package/dist/commands/list/list.js.map +1 -0
  87. package/dist/commands/list/print.js +301 -0
  88. package/dist/commands/list/print.js.map +1 -0
  89. package/dist/commands/list/review.js +120 -0
  90. package/dist/commands/list/review.js.map +1 -0
  91. package/dist/commands/list/views.js +105 -0
  92. package/dist/commands/list/views.js.map +1 -0
  93. package/dist/commands/mcp/dispatch.js +183 -0
  94. package/dist/commands/mcp/dispatch.js.map +1 -0
  95. package/dist/commands/mcp/mcp.js +34 -0
  96. package/dist/commands/mcp/mcp.js.map +1 -0
  97. package/dist/commands/mcp/serve.js +55 -0
  98. package/dist/commands/mcp/serve.js.map +1 -0
  99. package/dist/commands/migrate-openspec/materialize/active.js +170 -0
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
  101. package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
  102. package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
  103. package/dist/commands/migrate-openspec/materialize/living.js +62 -0
  104. package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
  105. package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
  106. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
  107. package/dist/commands/migrate-openspec/materialize/target.js +219 -0
  108. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
  109. package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
  110. package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
  111. package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
  112. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
  113. package/dist/commands/migrate-openspec/openspec/error.js +19 -0
  114. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
  115. package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
  116. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
  117. package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
  118. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
  119. package/dist/commands/migrate-openspec/openspec/read.js +49 -0
  120. package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
  121. package/dist/commands/migrate-openspec/print.js +89 -0
  122. package/dist/commands/migrate-openspec/print.js.map +1 -0
  123. package/dist/commands/new/commit.js +76 -0
  124. package/dist/commands/new/commit.js.map +1 -0
  125. package/dist/commands/new/new.js +283 -0
  126. package/dist/commands/new/new.js.map +1 -0
  127. package/dist/commands/new/templates.js +355 -0
  128. package/dist/commands/new/templates.js.map +1 -0
  129. package/dist/commands/open.js +203 -0
  130. package/dist/commands/open.js.map +1 -0
  131. package/dist/commands/policy/format.js +152 -0
  132. package/dist/commands/policy/format.js.map +1 -0
  133. package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
  134. package/dist/commands/policy/gate.js.map +1 -0
  135. package/dist/commands/rebase/edit.js +106 -0
  136. package/dist/commands/rebase/edit.js.map +1 -0
  137. package/dist/commands/rebase/living.js +185 -0
  138. package/dist/commands/rebase/living.js.map +1 -0
  139. package/dist/commands/rebase/plan.js +245 -0
  140. package/dist/commands/rebase/plan.js.map +1 -0
  141. package/dist/commands/rebase/rebase.js +275 -0
  142. package/dist/commands/rebase/rebase.js.map +1 -0
  143. package/dist/commands/seed/commit.js +192 -0
  144. package/dist/commands/seed/commit.js.map +1 -0
  145. package/dist/commands/seed/plan.js +133 -0
  146. package/dist/commands/seed/plan.js.map +1 -0
  147. package/dist/commands/seed/seed.js +172 -0
  148. package/dist/commands/seed/seed.js.map +1 -0
  149. package/dist/commands/show/feature.js +178 -0
  150. package/dist/commands/show/feature.js.map +1 -0
  151. package/dist/commands/show/marks.js +24 -0
  152. package/dist/commands/show/marks.js.map +1 -0
  153. package/dist/commands/{show.js → show/service.js} +63 -152
  154. package/dist/commands/show/service.js.map +1 -0
  155. package/dist/commands/show/show.js +77 -0
  156. package/dist/commands/show/show.js.map +1 -0
  157. package/dist/commands/status/json.js +45 -0
  158. package/dist/commands/status/json.js.map +1 -0
  159. package/dist/commands/status/print.js +157 -0
  160. package/dist/commands/status/print.js.map +1 -0
  161. package/dist/commands/status/status.js +119 -0
  162. package/dist/commands/status/status.js.map +1 -0
  163. package/dist/commands/steps/steps.js +189 -0
  164. package/dist/commands/steps/steps.js.map +1 -0
  165. package/dist/commands/subsystem/edit.js +192 -0
  166. package/dist/commands/subsystem/edit.js.map +1 -0
  167. package/dist/commands/subsystem/move.js +265 -0
  168. package/dist/commands/subsystem/move.js.map +1 -0
  169. package/dist/commands/subsystem/query.js +115 -0
  170. package/dist/commands/subsystem/query.js.map +1 -0
  171. package/dist/commands/subsystem/subsystem.js +101 -0
  172. package/dist/commands/subsystem/subsystem.js.map +1 -0
  173. package/dist/commands/subsystem/sync.js +103 -0
  174. package/dist/commands/subsystem/sync.js.map +1 -0
  175. package/dist/commands/subsystem/txn/txn.js +94 -0
  176. package/dist/commands/subsystem/txn/txn.js.map +1 -0
  177. package/dist/commands/subsystem/txn/views.js +70 -0
  178. package/dist/commands/subsystem/txn/views.js.map +1 -0
  179. package/dist/commands/unarchive/manifest.js +160 -0
  180. package/dist/commands/unarchive/manifest.js.map +1 -0
  181. package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
  182. package/dist/commands/unarchive/restore.js.map +1 -0
  183. package/dist/commands/unarchive/unarchive.js +67 -0
  184. package/dist/commands/unarchive/unarchive.js.map +1 -0
  185. package/dist/commands/validate/arch-coverage.js +204 -0
  186. package/dist/commands/validate/arch-coverage.js.map +1 -0
  187. package/dist/commands/validate/checks/fleet-shape.js +296 -0
  188. package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
  189. package/dist/commands/validate/checks/health-deps.js +55 -0
  190. package/dist/commands/validate/checks/health-deps.js.map +1 -0
  191. package/dist/commands/validate/checks/requirements.js +254 -0
  192. package/dist/commands/validate/checks/requirements.js.map +1 -0
  193. package/dist/commands/validate/checks/sources.js +76 -0
  194. package/dist/commands/validate/checks/sources.js.map +1 -0
  195. package/dist/commands/validate/checks/vocabulary.js +31 -0
  196. package/dist/commands/validate/checks/vocabulary.js.map +1 -0
  197. package/dist/commands/validate/feature.js +216 -0
  198. package/dist/commands/validate/feature.js.map +1 -0
  199. package/dist/commands/validate/fleet/census.js +69 -0
  200. package/dist/commands/validate/fleet/census.js.map +1 -0
  201. package/dist/commands/validate/fleet/kind-tags.js +39 -0
  202. package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
  203. package/dist/commands/validate/fleet/landscape.js +353 -0
  204. package/dist/commands/validate/fleet/landscape.js.map +1 -0
  205. package/dist/commands/validate/fleet/load.js +52 -0
  206. package/dist/commands/validate/fleet/load.js.map +1 -0
  207. package/dist/commands/validate/fleet/obligations.js +270 -0
  208. package/dist/commands/validate/fleet/obligations.js.map +1 -0
  209. package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
  210. package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
  211. package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
  212. package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
  213. package/dist/commands/validate/fleet/scorecard/print.js +63 -0
  214. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
  215. package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
  216. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
  217. package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
  218. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
  219. package/dist/commands/validate/fleet/usecases/place.js +13 -0
  220. package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
  221. package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
  222. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
  223. package/dist/commands/validate/fleet/usecases/steps.js +197 -0
  224. package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
  225. package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
  226. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
  227. package/dist/commands/validate/fleet/views/ids.js +40 -0
  228. package/dist/commands/validate/fleet/views/ids.js.map +1 -0
  229. package/dist/commands/validate/fleet/views/stale.js +59 -0
  230. package/dist/commands/validate/fleet/views/stale.js.map +1 -0
  231. package/dist/commands/validate/links/corpus.js +92 -0
  232. package/dist/commands/validate/links/corpus.js.map +1 -0
  233. package/dist/commands/validate/links/glossary.js +75 -0
  234. package/dist/commands/validate/links/glossary.js.map +1 -0
  235. package/dist/commands/validate/report.js +269 -0
  236. package/dist/commands/validate/report.js.map +1 -0
  237. package/dist/commands/validate/service/api.js +338 -0
  238. package/dist/commands/validate/service/api.js.map +1 -0
  239. package/dist/commands/validate/service/events/events.js +219 -0
  240. package/dist/commands/validate/service/events/events.js.map +1 -0
  241. package/dist/commands/validate/service/events/message-producers.js +77 -0
  242. package/dist/commands/validate/service/events/message-producers.js.map +1 -0
  243. package/dist/commands/validate/service/evidence-pins.js +131 -0
  244. package/dist/commands/validate/service/evidence-pins.js.map +1 -0
  245. package/dist/commands/validate/service/service.js +251 -0
  246. package/dist/commands/validate/service/service.js.map +1 -0
  247. package/dist/commands/validate/service/specs.js +251 -0
  248. package/dist/commands/validate/service/specs.js.map +1 -0
  249. package/dist/commands/validate/service/spine.js +113 -0
  250. package/dist/commands/validate/service/spine.js.map +1 -0
  251. package/dist/commands/validate/validate.js +338 -0
  252. package/dist/commands/validate/validate.js.map +1 -0
  253. package/dist/commands/verify/cross/diff.js +182 -0
  254. package/dist/commands/verify/cross/diff.js.map +1 -0
  255. package/dist/commands/verify/evidence/contract.js +32 -0
  256. package/dist/commands/verify/evidence/contract.js.map +1 -0
  257. package/dist/commands/verify/evidence/pins.js +52 -0
  258. package/dist/commands/verify/evidence/pins.js.map +1 -0
  259. package/dist/commands/verify/evidence/read.js +72 -0
  260. package/dist/commands/verify/evidence/read.js.map +1 -0
  261. package/dist/commands/verify/frozen.js +241 -0
  262. package/dist/commands/verify/frozen.js.map +1 -0
  263. package/dist/commands/verify/record.js +226 -0
  264. package/dist/commands/verify/record.js.map +1 -0
  265. package/dist/commands/verify/report.js +223 -0
  266. package/dist/commands/verify/report.js.map +1 -0
  267. package/dist/commands/verify/results.js +246 -0
  268. package/dist/commands/verify/results.js.map +1 -0
  269. package/dist/commands/verify/verify.js +217 -0
  270. package/dist/commands/verify/verify.js.map +1 -0
  271. package/dist/commands/vouch/contract.js +11 -0
  272. package/dist/commands/vouch/contract.js.map +1 -0
  273. package/dist/commands/vouch/pack/contract.js +2 -0
  274. package/dist/commands/vouch/pack/contract.js.map +1 -0
  275. package/dist/commands/vouch/pack/pack.js +266 -0
  276. package/dist/commands/vouch/pack/pack.js.map +1 -0
  277. package/dist/commands/vouch/pack/print.js +271 -0
  278. package/dist/commands/vouch/pack/print.js.map +1 -0
  279. package/dist/commands/vouch/pack/sections.js +142 -0
  280. package/dist/commands/vouch/pack/sections.js.map +1 -0
  281. package/dist/commands/vouch/prompt/confirm.js +46 -0
  282. package/dist/commands/vouch/prompt/confirm.js.map +1 -0
  283. package/dist/commands/vouch/report.js +101 -0
  284. package/dist/commands/vouch/report.js.map +1 -0
  285. package/dist/commands/vouch/run.js +285 -0
  286. package/dist/commands/vouch/run.js.map +1 -0
  287. package/dist/commands/vouch/sample/plan.js +125 -0
  288. package/dist/commands/vouch/sample/plan.js.map +1 -0
  289. package/dist/commands/vouch/sample/print.js +109 -0
  290. package/dist/commands/vouch/sample/print.js.map +1 -0
  291. package/dist/commands/vouch/vet/verify.js +123 -0
  292. package/dist/commands/vouch/vet/verify.js.map +1 -0
  293. package/dist/commands/vouch/vouch.js +176 -0
  294. package/dist/commands/vouch/vouch.js.map +1 -0
  295. package/dist/core/agent/agents-md/artifacts.js +220 -0
  296. package/dist/core/agent/agents-md/artifacts.js.map +1 -0
  297. package/dist/core/agent/agents-md/command-map.js +324 -0
  298. package/dist/core/agent/agents-md/command-map.js.map +1 -0
  299. package/dist/core/agent/agents-md/cycle.js +165 -0
  300. package/dist/core/agent/agents-md/cycle.js.map +1 -0
  301. package/dist/core/agent/agents-md/map/explain.js +34 -0
  302. package/dist/core/agent/agents-md/map/explain.js.map +1 -0
  303. package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
  304. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
  305. package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
  306. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
  307. package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
  308. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
  309. package/dist/core/agent/agents-md/map/mcp.js +41 -0
  310. package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
  311. package/dist/core/agent/agents-md/map/status.js +62 -0
  312. package/dist/core/agent/agents-md/map/status.js.map +1 -0
  313. package/dist/core/agent/agents-md/map/subsystem.js +32 -0
  314. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
  315. package/dist/core/agent/agents-md/map/usecases.js +60 -0
  316. package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
  317. package/dist/core/agent/agents-md/refusals.js +246 -0
  318. package/dist/core/agent/agents-md/refusals.js.map +1 -0
  319. package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
  320. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
  321. package/dist/core/agent/agents-md/spine.js +40 -0
  322. package/dist/core/agent/agents-md/spine.js.map +1 -0
  323. package/dist/core/agent/agents-md.js +170 -0
  324. package/dist/core/agent/agents-md.js.map +1 -0
  325. package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +50 -0
  326. package/dist/core/agent/agents-stamp.js.map +1 -0
  327. package/dist/core/agent/contract.js +2 -0
  328. package/dist/core/agent/contract.js.map +1 -0
  329. package/dist/core/agent/protocol.js +193 -0
  330. package/dist/core/agent/protocol.js.map +1 -0
  331. package/dist/core/agent/scaffold.js +259 -0
  332. package/dist/core/agent/scaffold.js.map +1 -0
  333. package/dist/core/agent/support/report.js +103 -0
  334. package/dist/core/agent/support/report.js.map +1 -0
  335. package/dist/core/agent/tools/dialects.js +150 -0
  336. package/dist/core/agent/tools/dialects.js.map +1 -0
  337. package/dist/core/agent/tools/registry.js +228 -0
  338. package/dist/core/agent/tools/registry.js.map +1 -0
  339. package/dist/core/agent/workflows/adopt.js +133 -0
  340. package/dist/core/agent/workflows/adopt.js.map +1 -0
  341. package/dist/core/agent/workflows/check.js +337 -0
  342. package/dist/core/agent/workflows/check.js.map +1 -0
  343. package/dist/core/agent/workflows/closing.js +262 -0
  344. package/dist/core/agent/workflows/closing.js.map +1 -0
  345. package/dist/core/agent/workflows/feature.js +105 -0
  346. package/dist/core/agent/workflows/feature.js.map +1 -0
  347. package/dist/core/agent/workflows/implement.js +105 -0
  348. package/dist/core/agent/workflows/implement.js.map +1 -0
  349. package/dist/core/agent/workflows/reference/authoring.js +209 -0
  350. package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
  351. package/dist/core/agent/workflows/reference/codes.js +65 -0
  352. package/dist/core/agent/workflows/reference/codes.js.map +1 -0
  353. package/dist/core/agent/workflows/reference/done-check.js +188 -0
  354. package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
  355. package/dist/core/agent/workflows/reference/reference.js +16 -0
  356. package/dist/core/agent/workflows/reference/reference.js.map +1 -0
  357. package/dist/core/agent/workflows/reference/spine.js +297 -0
  358. package/dist/core/agent/workflows/reference/spine.js.map +1 -0
  359. package/dist/core/asyncapi/baseline/plan.js +110 -0
  360. package/dist/core/asyncapi/baseline/plan.js.map +1 -0
  361. package/dist/core/asyncapi/baseline/surfaces.js +54 -0
  362. package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
  363. package/dist/core/asyncapi/depth.js +164 -0
  364. package/dist/core/asyncapi/depth.js.map +1 -0
  365. package/dist/core/asyncapi/digest.js +141 -0
  366. package/dist/core/asyncapi/digest.js.map +1 -0
  367. package/dist/core/asyncapi/merge/components.js +91 -0
  368. package/dist/core/asyncapi/merge/components.js.map +1 -0
  369. package/dist/core/asyncapi/merge/error.js +33 -0
  370. package/dist/core/asyncapi/merge/error.js.map +1 -0
  371. package/dist/core/asyncapi/merge/markers.js +308 -0
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -0
  373. package/dist/core/asyncapi/merge/merge.js +215 -0
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -0
  375. package/dist/core/asyncapi/merge/pin.js +107 -0
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -0
  377. package/dist/core/asyncapi/model.js +11 -0
  378. package/dist/core/asyncapi/model.js.map +1 -0
  379. package/dist/core/asyncapi/producers.js +50 -0
  380. package/dist/core/asyncapi/producers.js.map +1 -0
  381. package/dist/core/{asyncapi.js → asyncapi/read.js} +95 -109
  382. package/dist/core/asyncapi/read.js.map +1 -0
  383. package/dist/core/brief/brief.js +225 -0
  384. package/dist/core/brief/brief.js.map +1 -0
  385. package/dist/core/brief/checks.js +250 -0
  386. package/dist/core/brief/checks.js.map +1 -0
  387. package/dist/core/brief/landscape.js +194 -0
  388. package/dist/core/brief/landscape.js.map +1 -0
  389. package/dist/core/brief/targets.js +226 -0
  390. package/dist/core/brief/targets.js.map +1 -0
  391. package/dist/core/brief/unchecked.js +48 -0
  392. package/dist/core/brief/unchecked.js.map +1 -0
  393. package/dist/core/c4/arch.js +174 -0
  394. package/dist/core/c4/arch.js.map +1 -0
  395. package/dist/core/c4/likec4.js +90 -0
  396. package/dist/core/c4/likec4.js.map +1 -0
  397. package/dist/core/c4/parsed/deployment.js +158 -0
  398. package/dist/core/c4/parsed/deployment.js.map +1 -0
  399. package/dist/core/c4/parsed/dynamic-views.js +132 -0
  400. package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
  401. package/dist/core/c4/parsed/specification.js +78 -0
  402. package/dist/core/c4/parsed/specification.js.map +1 -0
  403. package/dist/core/c4/parsed/values.js +57 -0
  404. package/dist/core/c4/parsed/values.js.map +1 -0
  405. package/dist/core/c4/parsed/view-ids.js +75 -0
  406. package/dist/core/c4/parsed/view-ids.js.map +1 -0
  407. package/dist/core/c4/project/architecture.js +45 -0
  408. package/dist/core/c4/project/architecture.js.map +1 -0
  409. package/dist/core/c4/project/documents.js +58 -0
  410. package/dist/core/c4/project/documents.js.map +1 -0
  411. package/dist/core/c4/project/load.js +187 -0
  412. package/dist/core/c4/project/load.js.map +1 -0
  413. package/dist/core/c4/project/staged.js +220 -0
  414. package/dist/core/c4/project/staged.js.map +1 -0
  415. package/dist/core/c4/resolve/service.js +159 -0
  416. package/dist/core/c4/resolve/service.js.map +1 -0
  417. package/dist/core/c4/resolve/steps.js +144 -0
  418. package/dist/core/c4/resolve/steps.js.map +1 -0
  419. package/dist/core/c4/seed/fleet-file.js +201 -0
  420. package/dist/core/c4/seed/fleet-file.js.map +1 -0
  421. package/dist/core/c4/seed/items.js +91 -0
  422. package/dist/core/c4/seed/items.js.map +1 -0
  423. package/dist/core/c4/seed/stamp.js +48 -0
  424. package/dist/core/c4/seed/stamp.js.map +1 -0
  425. package/dist/core/c4/seed/template.js +109 -0
  426. package/dist/core/c4/seed/template.js.map +1 -0
  427. package/dist/core/c4/source-mask.js +82 -0
  428. package/dist/core/c4/source-mask.js.map +1 -0
  429. package/dist/core/c4/source-scan.js +227 -0
  430. package/dist/core/c4/source-scan.js.map +1 -0
  431. package/dist/core/c4/splice/authored-source.js +101 -0
  432. package/dist/core/c4/splice/authored-source.js.map +1 -0
  433. package/dist/core/c4/splice/contract.js +10 -0
  434. package/dist/core/c4/splice/contract.js.map +1 -0
  435. package/dist/core/c4/splice/delta-blocks.js +139 -0
  436. package/dist/core/c4/splice/delta-blocks.js.map +1 -0
  437. package/dist/core/c4/splice/identity/edges.js +36 -0
  438. package/dist/core/c4/splice/identity/edges.js.map +1 -0
  439. package/dist/core/c4/splice/landscape-merge.js +269 -0
  440. package/dist/core/c4/splice/landscape-merge.js.map +1 -0
  441. package/dist/core/c4/splice/placement.js +194 -0
  442. package/dist/core/c4/splice/placement.js.map +1 -0
  443. package/dist/core/c4/workspace.js +174 -0
  444. package/dist/core/c4/workspace.js.map +1 -0
  445. package/dist/core/capabilities/authoring/notes.js +141 -0
  446. package/dist/core/capabilities/authoring/notes.js.map +1 -0
  447. package/dist/core/capabilities/capabilities.js +124 -0
  448. package/dist/core/capabilities/capabilities.js.map +1 -0
  449. package/dist/core/capabilities/delta/doc.js +50 -0
  450. package/dist/core/capabilities/delta/doc.js.map +1 -0
  451. package/dist/core/capabilities/delta/overlay.js +129 -0
  452. package/dist/core/capabilities/delta/overlay.js.map +1 -0
  453. package/dist/core/capabilities/delta/summary.js +57 -0
  454. package/dist/core/capabilities/delta/summary.js.map +1 -0
  455. package/dist/core/capabilities/delta/tree.js +43 -0
  456. package/dist/core/capabilities/delta/tree.js.map +1 -0
  457. package/dist/core/capabilities/delta/uncovered.js +222 -0
  458. package/dist/core/capabilities/delta/uncovered.js.map +1 -0
  459. package/dist/core/capabilities/findings.js +342 -0
  460. package/dist/core/capabilities/findings.js.map +1 -0
  461. package/dist/core/capabilities/realizes/findings.js +181 -0
  462. package/dist/core/capabilities/realizes/findings.js.map +1 -0
  463. package/dist/core/capabilities/realizes/join.js +143 -0
  464. package/dist/core/capabilities/realizes/join.js.map +1 -0
  465. package/dist/core/capabilities/rollup.js +199 -0
  466. package/dist/core/capabilities/rollup.js.map +1 -0
  467. package/dist/core/capabilities/tree.js +126 -0
  468. package/dist/core/capabilities/tree.js.map +1 -0
  469. package/dist/core/capabilities/usecase-join.js +277 -0
  470. package/dist/core/capabilities/usecase-join.js.map +1 -0
  471. package/dist/core/coherence/authoring/scaffold.js +177 -0
  472. package/dist/core/coherence/authoring/scaffold.js.map +1 -0
  473. package/dist/core/coherence/authoring/sentinels.js +74 -0
  474. package/dist/core/coherence/authoring/sentinels.js.map +1 -0
  475. package/dist/core/coherence/coherence.js +287 -0
  476. package/dist/core/coherence/coherence.js.map +1 -0
  477. package/dist/core/coherence/declared.js +269 -0
  478. package/dist/core/coherence/declared.js.map +1 -0
  479. package/dist/core/coherence/events/declared.js +206 -0
  480. package/dist/core/coherence/events/declared.js.map +1 -0
  481. package/dist/core/coherence/events/events.js +204 -0
  482. package/dist/core/coherence/events/events.js.map +1 -0
  483. package/dist/core/coherence/events/grades.js +166 -0
  484. package/dist/core/coherence/events/grades.js.map +1 -0
  485. package/dist/core/coherence/events/lookups.js +122 -0
  486. package/dist/core/coherence/events/lookups.js.map +1 -0
  487. package/dist/core/coherence/events/merged.js +40 -0
  488. package/dist/core/coherence/events/merged.js.map +1 -0
  489. package/dist/core/coherence/living.js +198 -0
  490. package/dist/core/coherence/living.js.map +1 -0
  491. package/dist/core/coherence/lookups.js +155 -0
  492. package/dist/core/coherence/lookups.js.map +1 -0
  493. package/dist/core/coherence/pending.js +39 -0
  494. package/dist/core/coherence/pending.js.map +1 -0
  495. package/dist/core/conflict-markers.js +60 -0
  496. package/dist/core/conflict-markers.js.map +1 -0
  497. package/dist/core/delta/claims.js +73 -0
  498. package/dist/core/delta/claims.js.map +1 -0
  499. package/dist/core/delta/delta.js +296 -0
  500. package/dist/core/delta/delta.js.map +1 -0
  501. package/dist/core/delta/document.js +177 -0
  502. package/dist/core/delta/document.js.map +1 -0
  503. package/dist/core/delta/scope.js +48 -0
  504. package/dist/core/delta/scope.js.map +1 -0
  505. package/dist/core/delta/select.js +237 -0
  506. package/dist/core/delta/select.js.map +1 -0
  507. package/dist/core/dependencies/dependencies.js +141 -0
  508. package/dist/core/dependencies/dependencies.js.map +1 -0
  509. package/dist/core/dependencies/facts.js +165 -0
  510. package/dist/core/dependencies/facts.js.map +1 -0
  511. package/dist/core/dependencies/fanin.js +88 -0
  512. package/dist/core/dependencies/fanin.js.map +1 -0
  513. package/dist/core/dependencies/graph.js +106 -0
  514. package/dist/core/dependencies/graph.js.map +1 -0
  515. package/dist/core/deployment/delta.js +188 -0
  516. package/dist/core/deployment/delta.js.map +1 -0
  517. package/dist/core/deployment/objects.js +145 -0
  518. package/dist/core/deployment/objects.js.map +1 -0
  519. package/dist/core/diff/base-git.js +148 -0
  520. package/dist/core/diff/base-git.js.map +1 -0
  521. package/dist/core/diff/base-state.js +209 -0
  522. package/dist/core/diff/base-state.js.map +1 -0
  523. package/dist/core/diff/current-state.js +96 -0
  524. package/dist/core/diff/current-state.js.map +1 -0
  525. package/dist/core/diff/scope/changed-paths.js +188 -0
  526. package/dist/core/diff/scope/changed-paths.js.map +1 -0
  527. package/dist/core/diff/semantic.js +243 -0
  528. package/dist/core/diff/semantic.js.map +1 -0
  529. package/dist/core/diff/victims.js +247 -0
  530. package/dist/core/diff/victims.js.map +1 -0
  531. package/dist/core/docs.js +7 -64
  532. package/dist/core/docs.js.map +1 -1
  533. package/dist/core/doctor/agents.js +222 -0
  534. package/dist/core/doctor/agents.js.map +1 -0
  535. package/dist/core/doctor/config.js +135 -0
  536. package/dist/core/doctor/config.js.map +1 -0
  537. package/dist/core/doctor/doctor.js +275 -0
  538. package/dist/core/doctor/doctor.js.map +1 -0
  539. package/dist/core/doctor/report.js +13 -0
  540. package/dist/core/doctor/report.js.map +1 -0
  541. package/dist/core/doctor/residue.js +110 -0
  542. package/dist/core/doctor/residue.js.map +1 -0
  543. package/dist/core/document/apply.js +77 -0
  544. package/dist/core/document/apply.js.map +1 -0
  545. package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
  546. package/dist/core/document/frontmatter.js.map +1 -0
  547. package/dist/core/document/grammar/directives.js +143 -0
  548. package/dist/core/document/grammar/directives.js.map +1 -0
  549. package/dist/core/document/parse.js +263 -0
  550. package/dist/core/document/parse.js.map +1 -0
  551. package/dist/core/document/scenarios.js +221 -0
  552. package/dist/core/document/scenarios.js.map +1 -0
  553. package/dist/core/document/spec.js +241 -0
  554. package/dist/core/document/spec.js.map +1 -0
  555. package/dist/core/{config.js → envelope/config.js} +79 -18
  556. package/dist/core/envelope/config.js.map +1 -0
  557. package/dist/core/envelope/json.js +175 -0
  558. package/dist/core/envelope/json.js.map +1 -0
  559. package/dist/core/{version.js → envelope/version.js} +3 -3
  560. package/dist/core/envelope/version.js.map +1 -0
  561. package/dist/core/explain/families.js +175 -0
  562. package/dist/core/explain/families.js.map +1 -0
  563. package/dist/core/explain/fix-tables.js +178 -0
  564. package/dist/core/explain/fix-tables.js.map +1 -0
  565. package/dist/core/explain/lookup.js +181 -0
  566. package/dist/core/explain/lookup.js.map +1 -0
  567. package/dist/core/explain/refusals.js +49 -0
  568. package/dist/core/explain/refusals.js.map +1 -0
  569. package/dist/core/explain/terms.js +144 -0
  570. package/dist/core/explain/terms.js.map +1 -0
  571. package/dist/core/explore/describe.js +105 -0
  572. package/dist/core/explore/describe.js.map +1 -0
  573. package/dist/core/{explore.js → explore/explore.js} +50 -101
  574. package/dist/core/explore/explore.js.map +1 -0
  575. package/dist/core/fleet-context.js +120 -78
  576. package/dist/core/fleet-context.js.map +1 -1
  577. package/dist/core/gate/checks.js +201 -0
  578. package/dist/core/gate/checks.js.map +1 -0
  579. package/dist/core/gate/partners.js +205 -0
  580. package/dist/core/gate/partners.js.map +1 -0
  581. package/dist/core/gate/report.js +47 -0
  582. package/dist/core/gate/report.js.map +1 -0
  583. package/dist/core/gate/verification.js +113 -0
  584. package/dist/core/gate/verification.js.map +1 -0
  585. package/dist/core/gherkin/digest.js +60 -0
  586. package/dist/core/gherkin/digest.js.map +1 -0
  587. package/dist/core/gherkin/emit.js +177 -0
  588. package/dist/core/gherkin/emit.js.map +1 -0
  589. package/dist/core/gherkin/read.js +76 -0
  590. package/dist/core/gherkin/read.js.map +1 -0
  591. package/dist/core/gherkin/stale.js +234 -0
  592. package/dist/core/gherkin/stale.js.map +1 -0
  593. package/dist/core/gherkin/stamp.js +77 -0
  594. package/dist/core/gherkin/stamp.js.map +1 -0
  595. package/dist/core/gherkin/steps/catalogue.js +154 -0
  596. package/dist/core/gherkin/steps/catalogue.js.map +1 -0
  597. package/dist/core/gherkin/steps/inventory.js +98 -0
  598. package/dist/core/gherkin/steps/inventory.js.map +1 -0
  599. package/dist/core/gherkin/steps/phrase.js +72 -0
  600. package/dist/core/gherkin/steps/phrase.js.map +1 -0
  601. package/dist/core/glossary/delta.js +106 -0
  602. package/dist/core/glossary/delta.js.map +1 -0
  603. package/dist/core/glossary/tree.js +86 -0
  604. package/dist/core/glossary/tree.js.map +1 -0
  605. package/dist/core/kernel/concurrency.js.map +1 -0
  606. package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
  607. package/dist/core/kernel/document-bytes.js.map +1 -0
  608. package/dist/core/kernel/fences.js +42 -0
  609. package/dist/core/kernel/fences.js.map +1 -0
  610. package/dist/core/kernel/ids/capability.js +72 -0
  611. package/dist/core/kernel/ids/capability.js.map +1 -0
  612. package/dist/core/kernel/ids/dirs.js +33 -0
  613. package/dist/core/kernel/ids/dirs.js.map +1 -0
  614. package/dist/core/kernel/ids/feature.js +75 -0
  615. package/dist/core/kernel/ids/feature.js.map +1 -0
  616. package/dist/core/kernel/ids/service.js +156 -0
  617. package/dist/core/kernel/ids/service.js.map +1 -0
  618. package/dist/core/kernel/ids/subsystem.js +63 -0
  619. package/dist/core/kernel/ids/subsystem.js.map +1 -0
  620. package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
  621. package/dist/core/kernel/path-safety.js.map +1 -0
  622. package/dist/core/kernel/records.js.map +1 -0
  623. package/dist/core/links/backlinks.js +68 -0
  624. package/dist/core/links/backlinks.js.map +1 -0
  625. package/dist/core/links/case.js +74 -0
  626. package/dist/core/links/case.js.map +1 -0
  627. package/dist/core/links/corpus.js +104 -0
  628. package/dist/core/links/corpus.js.map +1 -0
  629. package/dist/core/links/findings.js +110 -0
  630. package/dist/core/links/findings.js.map +1 -0
  631. package/dist/core/links/parse.js +236 -0
  632. package/dist/core/links/parse.js.map +1 -0
  633. package/dist/core/mcp/argv.js +111 -0
  634. package/dist/core/mcp/argv.js.map +1 -0
  635. package/dist/core/mcp/author-tools.js +72 -0
  636. package/dist/core/mcp/author-tools.js.map +1 -0
  637. package/dist/core/mcp/framing.js +46 -0
  638. package/dist/core/mcp/framing.js.map +1 -0
  639. package/dist/core/mcp/protocol.js +280 -0
  640. package/dist/core/mcp/protocol.js.map +1 -0
  641. package/dist/core/mcp/tools.js +314 -0
  642. package/dist/core/mcp/tools.js.map +1 -0
  643. package/dist/core/obligations/obligations.js +106 -0
  644. package/dist/core/obligations/obligations.js.map +1 -0
  645. package/dist/core/openapi/baseline/gate.js +166 -0
  646. package/dist/core/openapi/baseline/gate.js.map +1 -0
  647. package/dist/core/openapi/baseline/plan.js +112 -0
  648. package/dist/core/openapi/baseline/plan.js.map +1 -0
  649. package/dist/core/openapi/baseline/record.js +175 -0
  650. package/dist/core/openapi/baseline/record.js.map +1 -0
  651. package/dist/core/openapi/depth.js +122 -0
  652. package/dist/core/openapi/depth.js.map +1 -0
  653. package/dist/core/openapi/digest.js +189 -0
  654. package/dist/core/openapi/digest.js.map +1 -0
  655. package/dist/core/{openapi.js → openapi/doc.js} +44 -106
  656. package/dist/core/openapi/doc.js.map +1 -0
  657. package/dist/core/openapi/merge/components.js +234 -0
  658. package/dist/core/openapi/merge/components.js.map +1 -0
  659. package/dist/core/openapi/merge/error.js +26 -0
  660. package/dist/core/openapi/merge/error.js.map +1 -0
  661. package/dist/core/openapi/merge/markers.js +208 -0
  662. package/dist/core/openapi/merge/markers.js.map +1 -0
  663. package/dist/core/openapi/merge/merge.js +256 -0
  664. package/dist/core/openapi/merge/merge.js.map +1 -0
  665. package/dist/core/openapi/merge/pin.js +128 -0
  666. package/dist/core/openapi/merge/pin.js.map +1 -0
  667. package/dist/core/openspec/decide/capabilities.js +244 -0
  668. package/dist/core/openspec/decide/capabilities.js.map +1 -0
  669. package/dist/core/openspec/decide/dispositions.js +63 -0
  670. package/dist/core/openspec/decide/dispositions.js.map +1 -0
  671. package/dist/core/openspec/decide/renames.js +179 -0
  672. package/dist/core/openspec/decide/renames.js.map +1 -0
  673. package/dist/core/openspec/decide/verdict.js +84 -0
  674. package/dist/core/openspec/decide/verdict.js.map +1 -0
  675. package/dist/core/openspec/inventory.js +221 -0
  676. package/dist/core/openspec/inventory.js.map +1 -0
  677. package/dist/core/openspec/model/mapping.js +76 -0
  678. package/dist/core/openspec/model/mapping.js.map +1 -0
  679. package/dist/core/openspec/model/model.js +65 -0
  680. package/dist/core/openspec/model/model.js.map +1 -0
  681. package/dist/core/openspec/scan/changes.js +235 -0
  682. package/dist/core/openspec/scan/changes.js.map +1 -0
  683. package/dist/core/openspec/scan/config.js +200 -0
  684. package/dist/core/openspec/scan/config.js.map +1 -0
  685. package/dist/core/openspec/scan/shape.js +260 -0
  686. package/dist/core/openspec/scan/shape.js.map +1 -0
  687. package/dist/core/openspec/scan/walk.js +133 -0
  688. package/dist/core/openspec/scan/walk.js.map +1 -0
  689. package/dist/core/openspec/scan/workspace.js +71 -0
  690. package/dist/core/openspec/scan/workspace.js.map +1 -0
  691. package/dist/core/owners/codeowners.js +141 -0
  692. package/dist/core/owners/codeowners.js.map +1 -0
  693. package/dist/core/pack/features.js +120 -0
  694. package/dist/core/pack/features.js.map +1 -0
  695. package/dist/core/pack/joins.js +112 -0
  696. package/dist/core/pack/joins.js.map +1 -0
  697. package/dist/core/pack/living.js +137 -0
  698. package/dist/core/pack/living.js.map +1 -0
  699. package/dist/core/pack/pack.js +76 -0
  700. package/dist/core/pack/pack.js.map +1 -0
  701. package/dist/core/permissions/permissions.js +87 -0
  702. package/dist/core/permissions/permissions.js.map +1 -0
  703. package/dist/core/projection/api.js +84 -0
  704. package/dist/core/projection/api.js.map +1 -0
  705. package/dist/core/projection/arch-slice.js +63 -0
  706. package/dist/core/projection/arch-slice.js.map +1 -0
  707. package/dist/core/projection/events.js +34 -0
  708. package/dist/core/projection/events.js.map +1 -0
  709. package/dist/core/provenance/findings.js +208 -0
  710. package/dist/core/provenance/findings.js.map +1 -0
  711. package/dist/core/provenance/git.js +225 -0
  712. package/dist/core/provenance/git.js.map +1 -0
  713. package/dist/core/provenance/gitq/moves.js +182 -0
  714. package/dist/core/provenance/gitq/moves.js.map +1 -0
  715. package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
  716. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
  717. package/dist/core/provenance/sample/scope.js +238 -0
  718. package/dist/core/provenance/sample/scope.js.map +1 -0
  719. package/dist/core/provenance/sample/sections.js +61 -0
  720. package/dist/core/provenance/sample/sections.js.map +1 -0
  721. package/dist/core/provenance/sources.js +263 -0
  722. package/dist/core/provenance/sources.js.map +1 -0
  723. package/dist/core/provenance/stamp.js +232 -0
  724. package/dist/core/provenance/stamp.js.map +1 -0
  725. package/dist/core/provenance/walk.js +157 -0
  726. package/dist/core/provenance/walk.js.map +1 -0
  727. package/dist/core/repo/authored/paths.js +210 -0
  728. package/dist/core/repo/authored/paths.js.map +1 -0
  729. package/dist/core/repo/entries.js +126 -0
  730. package/dist/core/repo/entries.js.map +1 -0
  731. package/dist/core/repo/paths.js +277 -0
  732. package/dist/core/repo/paths.js.map +1 -0
  733. package/dist/core/repo/repo.js +248 -0
  734. package/dist/core/repo/repo.js.map +1 -0
  735. package/dist/core/repo/service-target.js +153 -0
  736. package/dist/core/repo/service-target.js.map +1 -0
  737. package/dist/core/repo/state.js +58 -0
  738. package/dist/core/repo/state.js.map +1 -0
  739. package/dist/core/repo/tree/find.js +52 -0
  740. package/dist/core/repo/tree/find.js.map +1 -0
  741. package/dist/core/repo/tree/fs.js +64 -0
  742. package/dist/core/repo/tree/fs.js.map +1 -0
  743. package/dist/core/repo/tree/marker.js +61 -0
  744. package/dist/core/repo/tree/marker.js.map +1 -0
  745. package/dist/core/repo/tree/views.js +172 -0
  746. package/dist/core/repo/tree/views.js.map +1 -0
  747. package/dist/core/repo/tree/walk.js +234 -0
  748. package/dist/core/repo/tree/walk.js.map +1 -0
  749. package/dist/core/results.js +99 -2
  750. package/dist/core/results.js.map +1 -1
  751. package/dist/core/scaffold/landscape.js +131 -0
  752. package/dist/core/scaffold/landscape.js.map +1 -0
  753. package/dist/core/scaffold/readme.js +84 -0
  754. package/dist/core/scaffold/readme.js.map +1 -0
  755. package/dist/core/staging/commit.js +241 -0
  756. package/dist/core/staging/commit.js.map +1 -0
  757. package/dist/core/staging/interrupted.js +96 -0
  758. package/dist/core/staging/interrupted.js.map +1 -0
  759. package/dist/core/staging/lock.js +206 -0
  760. package/dist/core/staging/lock.js.map +1 -0
  761. package/dist/core/staging/recovery/finding.js +35 -0
  762. package/dist/core/staging/recovery/finding.js.map +1 -0
  763. package/dist/core/staging/recovery/intent.js +98 -0
  764. package/dist/core/staging/recovery/intent.js.map +1 -0
  765. package/dist/core/staging/recovery/recover.js +126 -0
  766. package/dist/core/staging/recovery/recover.js.map +1 -0
  767. package/dist/core/staging/recovery/residue.js +62 -0
  768. package/dist/core/staging/recovery/residue.js.map +1 -0
  769. package/dist/core/staging/snapshot.js +202 -0
  770. package/dist/core/staging/snapshot.js.map +1 -0
  771. package/dist/core/staging/txn/forward.js +171 -0
  772. package/dist/core/staging/txn/forward.js.map +1 -0
  773. package/dist/core/staging/txn/journal.js +139 -0
  774. package/dist/core/staging/txn/journal.js.map +1 -0
  775. package/dist/core/staging/txn/transaction.js +75 -0
  776. package/dist/core/staging/txn/transaction.js.map +1 -0
  777. package/dist/core/staging/writes.js +113 -0
  778. package/dist/core/staging/writes.js.map +1 -0
  779. package/dist/core/status/actions/execution.js +49 -0
  780. package/dist/core/status/actions/execution.js.map +1 -0
  781. package/dist/core/status/contracts.js +102 -0
  782. package/dist/core/status/contracts.js.map +1 -0
  783. package/dist/core/status/feature/artifacts.js +196 -0
  784. package/dist/core/status/feature/artifacts.js.map +1 -0
  785. package/dist/core/status/feature/feature.js +207 -0
  786. package/dist/core/status/feature/feature.js.map +1 -0
  787. package/dist/core/status/feature/next.js +150 -0
  788. package/dist/core/status/feature/next.js.map +1 -0
  789. package/dist/core/status/feature/state.js +21 -0
  790. package/dist/core/status/feature/state.js.map +1 -0
  791. package/dist/core/status/fleet/fleet.js +197 -0
  792. package/dist/core/status/fleet/fleet.js.map +1 -0
  793. package/dist/core/status/fleet/next.js +207 -0
  794. package/dist/core/status/fleet/next.js.map +1 -0
  795. package/dist/core/status/interrupted.js +97 -0
  796. package/dist/core/status/interrupted.js.map +1 -0
  797. package/dist/core/status/report.js +31 -0
  798. package/dist/core/status/report.js.map +1 -0
  799. package/dist/core/status/scan.js +41 -0
  800. package/dist/core/status/scan.js.map +1 -0
  801. package/dist/core/status/verification.js +201 -0
  802. package/dist/core/status/verification.js.map +1 -0
  803. package/dist/core/usecases/capability.js +169 -0
  804. package/dist/core/usecases/capability.js.map +1 -0
  805. package/dist/core/usecases/delta/claims.js +116 -0
  806. package/dist/core/usecases/delta/claims.js.map +1 -0
  807. package/dist/core/usecases/delta/flows.js +103 -0
  808. package/dist/core/usecases/delta/flows.js.map +1 -0
  809. package/dist/core/usecases/delta/overlay.js +72 -0
  810. package/dist/core/usecases/delta/overlay.js.map +1 -0
  811. package/dist/core/usecases/fleet.js +137 -0
  812. package/dist/core/usecases/fleet.js.map +1 -0
  813. package/dist/core/usecases/operations.js +111 -0
  814. package/dist/core/usecases/operations.js.map +1 -0
  815. package/dist/core/usecases/place.js +44 -0
  816. package/dist/core/usecases/place.js.map +1 -0
  817. package/dist/core/usecases/touch.js +101 -0
  818. package/dist/core/usecases/touch.js.map +1 -0
  819. package/dist/core/verify/answers.js +173 -0
  820. package/dist/core/verify/answers.js.map +1 -0
  821. package/dist/core/verify/build.js +150 -0
  822. package/dist/core/verify/build.js.map +1 -0
  823. package/dist/core/verify/checklist.js +201 -0
  824. package/dist/core/verify/checklist.js.map +1 -0
  825. package/dist/core/verify/claims/identity.js +61 -0
  826. package/dist/core/verify/claims/identity.js.map +1 -0
  827. package/dist/core/verify/cross/diff.js +97 -0
  828. package/dist/core/verify/cross/diff.js.map +1 -0
  829. package/dist/core/verify/evidence/contract.js +169 -0
  830. package/dist/core/verify/evidence/contract.js.map +1 -0
  831. package/dist/core/verify/file.js +257 -0
  832. package/dist/core/verify/file.js.map +1 -0
  833. package/dist/core/verify/pins/lint.js +157 -0
  834. package/dist/core/verify/pins/lint.js.map +1 -0
  835. package/dist/core/verify/pins/pin.js +95 -0
  836. package/dist/core/verify/pins/pin.js.map +1 -0
  837. package/dist/core/verify/record.js +142 -0
  838. package/dist/core/verify/record.js.map +1 -0
  839. package/dist/core/verify/store/commit.js +89 -0
  840. package/dist/core/verify/store/commit.js.map +1 -0
  841. package/dist/core/verify/store/render.js +57 -0
  842. package/dist/core/verify/store/render.js.map +1 -0
  843. package/dist/core/vocabulary/health.js +91 -0
  844. package/dist/core/vocabulary/health.js.map +1 -0
  845. package/dist/core/vocabulary/issue.js +58 -0
  846. package/dist/core/vocabulary/issue.js.map +1 -0
  847. package/dist/core/vocabulary/maturity.js +145 -0
  848. package/dist/core/vocabulary/maturity.js.map +1 -0
  849. package/dist/core/{report.js → vocabulary/report.js} +30 -2
  850. package/dist/core/vocabulary/report.js.map +1 -0
  851. package/dist/core/vocabulary/steps.js +266 -0
  852. package/dist/core/vocabulary/steps.js.map +1 -0
  853. package/dist/core/workspace/discover.js +239 -0
  854. package/dist/core/workspace/discover.js.map +1 -0
  855. package/dist/core/workspace/render.js +52 -0
  856. package/dist/core/workspace/render.js.map +1 -0
  857. package/examples/README.md +189 -0
  858. package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
  859. package/examples/docs/architecture/capabilities.yaml +31 -0
  860. package/examples/docs/architecture/landscape.likec4 +280 -0
  861. package/examples/docs/architecture/obligations.yaml +23 -0
  862. package/examples/docs/architecture/permissions.yaml +50 -0
  863. package/examples/docs/architecture/subsystems.likec4 +11 -0
  864. package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
  865. package/examples/docs/capabilities/checkout/spec.md +55 -0
  866. package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
  867. package/examples/docs/capabilities/order-notifications/spec.md +43 -0
  868. package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
  869. package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
  870. package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
  871. package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
  872. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
  873. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
  874. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
  875. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
  876. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
  877. package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
  878. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
  879. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
  880. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
  881. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
  882. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
  883. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
  884. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
  885. package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
  886. package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
  887. package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
  888. package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
  889. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
  890. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
  891. package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
  892. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
  893. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
  894. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
  895. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
  896. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
  897. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
  898. package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
  899. package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
  900. package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
  901. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
  902. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
  903. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
  904. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
  905. package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
  906. package/examples/docs/glossary/order.md +15 -0
  907. package/examples/docs/glossary/payments/authorization.md +14 -0
  908. package/examples/docs/likec4.config.json +9 -0
  909. package/examples/docs/loam.json +3 -0
  910. package/examples/docs/services/checkout-web/model.likec4 +109 -0
  911. package/examples/docs/services/checkout-web/spec.md +91 -0
  912. package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
  913. package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
  914. package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
  915. package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
  916. package/examples/docs/services/order-service/arch.spec.md +127 -0
  917. package/examples/docs/services/order-service/asyncapi.yaml +108 -0
  918. package/examples/docs/services/order-service/health.yaml +57 -0
  919. package/examples/docs/services/order-service/model.likec4 +138 -0
  920. package/examples/docs/services/order-service/openapi.yaml +257 -0
  921. package/examples/docs/services/order-service/runbook.md +62 -0
  922. package/examples/docs/services/order-service/spec.md +134 -0
  923. package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
  924. package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
  925. package/examples/docs/services/payment-service/arch.spec.md +73 -0
  926. package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
  927. package/examples/docs/services/payment-service/health.yaml +39 -0
  928. package/examples/docs/services/payment-service/model.likec4 +67 -0
  929. package/examples/docs/services/payment-service/openapi.yaml +151 -0
  930. package/examples/docs/services/payment-service/runbook.md +37 -0
  931. package/examples/docs/services/payment-service/spec.md +88 -0
  932. package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
  933. package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
  934. package/examples/docs/services/platform/identity-service/health.yaml +70 -0
  935. package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
  936. package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
  937. package/examples/docs/services/platform/identity-service/runbook.md +95 -0
  938. package/examples/docs/services/platform/identity-service/spec.md +119 -0
  939. package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
  940. package/examples/docs/services/platform/notification-service/health.yaml +35 -0
  941. package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
  942. package/examples/docs/services/platform/notification-service/runbook.md +48 -0
  943. package/examples/docs/services/platform/notification-service/spec.md +112 -0
  944. package/examples/docs/services/platform/subsystem.yaml +2 -0
  945. package/examples/fleet.yaml +57 -0
  946. package/package.json +19 -6
  947. package/dist/commands/adopt.js +0 -240
  948. package/dist/commands/adopt.js.map +0 -1
  949. package/dist/commands/archive.js +0 -1313
  950. package/dist/commands/archive.js.map +0 -1
  951. package/dist/commands/delta.js +0 -388
  952. package/dist/commands/delta.js.map +0 -1
  953. package/dist/commands/docs-repo-gate.js.map +0 -1
  954. package/dist/commands/format.js +0 -17
  955. package/dist/commands/format.js.map +0 -1
  956. package/dist/commands/gherkin.js +0 -309
  957. package/dist/commands/gherkin.js.map +0 -1
  958. package/dist/commands/init.js.map +0 -1
  959. package/dist/commands/list.js +0 -295
  960. package/dist/commands/list.js.map +0 -1
  961. package/dist/commands/migrate-openspec.js +0 -950
  962. package/dist/commands/migrate-openspec.js.map +0 -1
  963. package/dist/commands/new.js +0 -413
  964. package/dist/commands/new.js.map +0 -1
  965. package/dist/commands/rebase.js +0 -356
  966. package/dist/commands/rebase.js.map +0 -1
  967. package/dist/commands/show.js.map +0 -1
  968. package/dist/commands/status.js +0 -262
  969. package/dist/commands/status.js.map +0 -1
  970. package/dist/commands/unarchive.js.map +0 -1
  971. package/dist/commands/validate.js +0 -1773
  972. package/dist/commands/validate.js.map +0 -1
  973. package/dist/commands/verify.js +0 -775
  974. package/dist/commands/verify.js.map +0 -1
  975. package/dist/commands/vouch.js +0 -421
  976. package/dist/commands/vouch.js.map +0 -1
  977. package/dist/core/agent.js +0 -2173
  978. package/dist/core/agent.js.map +0 -1
  979. package/dist/core/agents-stamp.js.map +0 -1
  980. package/dist/core/arch.js +0 -130
  981. package/dist/core/arch.js.map +0 -1
  982. package/dist/core/asyncapi.js.map +0 -1
  983. package/dist/core/brief.js +0 -755
  984. package/dist/core/brief.js.map +0 -1
  985. package/dist/core/coherence.js +0 -577
  986. package/dist/core/coherence.js.map +0 -1
  987. package/dist/core/concurrency.js.map +0 -1
  988. package/dist/core/config.js.map +0 -1
  989. package/dist/core/delta.js +0 -476
  990. package/dist/core/delta.js.map +0 -1
  991. package/dist/core/dependencies.js +0 -356
  992. package/dist/core/dependencies.js.map +0 -1
  993. package/dist/core/doctor.js +0 -596
  994. package/dist/core/doctor.js.map +0 -1
  995. package/dist/core/document-bytes.js.map +0 -1
  996. package/dist/core/explore.js.map +0 -1
  997. package/dist/core/frontmatter.js.map +0 -1
  998. package/dist/core/gherkin.js +0 -463
  999. package/dist/core/gherkin.js.map +0 -1
  1000. package/dist/core/health.js +0 -67
  1001. package/dist/core/health.js.map +0 -1
  1002. package/dist/core/ids.js +0 -122
  1003. package/dist/core/ids.js.map +0 -1
  1004. package/dist/core/issue.js +0 -9
  1005. package/dist/core/issue.js.map +0 -1
  1006. package/dist/core/json.js +0 -74
  1007. package/dist/core/json.js.map +0 -1
  1008. package/dist/core/likec4.js +0 -489
  1009. package/dist/core/likec4.js.map +0 -1
  1010. package/dist/core/maturity.js +0 -66
  1011. package/dist/core/maturity.js.map +0 -1
  1012. package/dist/core/openapi-merge.js +0 -552
  1013. package/dist/core/openapi-merge.js.map +0 -1
  1014. package/dist/core/openapi.js.map +0 -1
  1015. package/dist/core/openspec-inventory.js +0 -1440
  1016. package/dist/core/openspec-inventory.js.map +0 -1
  1017. package/dist/core/path-safety.js.map +0 -1
  1018. package/dist/core/provenance.js +0 -787
  1019. package/dist/core/provenance.js.map +0 -1
  1020. package/dist/core/records.js.map +0 -1
  1021. package/dist/core/repo.js +0 -438
  1022. package/dist/core/repo.js.map +0 -1
  1023. package/dist/core/report.js.map +0 -1
  1024. package/dist/core/spec.js +0 -505
  1025. package/dist/core/spec.js.map +0 -1
  1026. package/dist/core/staging.js +0 -890
  1027. package/dist/core/staging.js.map +0 -1
  1028. package/dist/core/status.js +0 -1132
  1029. package/dist/core/status.js.map +0 -1
  1030. package/dist/core/steps.js +0 -72
  1031. package/dist/core/steps.js.map +0 -1
  1032. package/dist/core/verify.js +0 -807
  1033. package/dist/core/verify.js.map +0 -1
  1034. package/dist/core/version.js.map +0 -1
  1035. /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
  1036. /package/dist/core/{records.js → kernel/records.js} +0 -0
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The journaled commit itself: journal (fsynced) → swap → renames → clear,
3
+ * with archive's ordering on the failure path — the rollback decides the
4
+ * outcome, and only then does the journal have nothing left to describe. A
5
+ * crash anywhere inside leaves either the journal plus a recoverable tree
6
+ * (roll forward finishes it) or a fully rolled-back tree whose stale journal
7
+ * the next recovery reads as the consistent pre-state.
8
+ *
9
+ * Callers stage for themselves before committing, on purpose: vouch's
10
+ * byte-level raced check and gherkin's planned-vs-read compare both have to
11
+ * run BETWEEN staging and the swap, against the pre-images staging read.
12
+ *
13
+ * The DIRECTORY RENAMES (`spec.moves`, `subsystem move`'s half) come after
14
+ * every file swap, deliberately: the generated views file must land in the
15
+ * same commit as the renames or the tree is left failing between two states,
16
+ * and a failure mid-renames still has the swapped files' pre-images in hand
17
+ * to roll everything back together — renames undone newest-first, then the
18
+ * files, one failure list, one verdict.
19
+ */
20
+ import { rename } from "node:fs/promises";
21
+ import { message, rollbackMessage, rollbackStaged, StagingRaceError, swapStaged } from "../commit.js";
22
+ import {} from "../writes.js";
23
+ import { clearTxnIntent, writeTxnIntent } from "./journal.js";
24
+ /**
25
+ * `what` is the verb the rollback sentence uses for the half-state ("merged",
26
+ * "stamped", "written") — each caller keeps the word its operators already
27
+ * grep their logs for.
28
+ */
29
+ export async function commitStaged(spec, staged, what) {
30
+ await writeTxnIntent(spec, staged);
31
+ const performed = [];
32
+ try {
33
+ await swapStaged(staged);
34
+ for (const move of spec.moves ?? []) {
35
+ await rename(move.from, move.to);
36
+ performed.push(move);
37
+ }
38
+ }
39
+ catch (err) {
40
+ // Renames back first, newest-first: each is its own inverse, and a
41
+ // directory that cannot go back is exactly as reportable as a file that
42
+ // cannot — it joins the same failure list and forces the same verdict.
43
+ const failures = [];
44
+ for (const move of [...performed].reverse()) {
45
+ try {
46
+ await rename(move.to, move.from);
47
+ }
48
+ catch (undoErr) {
49
+ failures.push(`${move.to} (${message(undoErr)})`);
50
+ }
51
+ }
52
+ failures.push(...(await rollbackStaged(staged)));
53
+ // The journal is cleared only when the rollback HELD. On rollback-incomplete
54
+ // the tree is genuinely half-written and the temps are gone — the journal
55
+ // is the one durable record of which files, so doctor keeps naming them,
56
+ // validate keeps refusing to grade the tree green, and the next writer
57
+ // refuses commit-interrupted instead of writing over the wreckage. Archive
58
+ // pays the same way by retaining its snapshot on this path.
59
+ if (failures.length === 0)
60
+ await clearTxnIntent(spec.root);
61
+ const raced = err instanceof StagingRaceError || err.code === "EEXIST";
62
+ if (failures.length > 0) {
63
+ // NEVER raced, whatever threw: callers remap a raced failure onto their
64
+ // own softer code (vouch-raced, already-exists) with "nothing was
65
+ // written" prose — and here something WAS written and could not be
66
+ // taken back. rollback-incomplete is the one answer that demands a
67
+ // human, and no remap may swallow the file list that says where.
68
+ return { ok: false, code: "rollback-incomplete", message: rollbackMessage(err, failures, what), raced: false };
69
+ }
70
+ return { ok: false, code: "merge-failed", message: `${message(err)} — rolled back, nothing was written`, raced };
71
+ }
72
+ await clearTxnIntent(spec.root);
73
+ return { ok: true };
74
+ }
75
+ //# sourceMappingURL=transaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction.js","sourceRoot":"","sources":["../../../../src/core/staging/txn/transaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACtG,OAAO,EAAoB,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAgB,MAAM,cAAc,CAAC;AAkB5E;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAa,EAAE,MAAqB,EAAE,IAAY;IACnF,MAAM,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,MAAM,SAAS,GAAmC,EAAE,CAAC;IACrD,IAAI,CAAC;QACH,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;QACzB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;YACpC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;YACjC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,mEAAmE;QACnE,wEAAwE;QACxE,uEAAuE;QACvE,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;YAC5C,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YACnC,CAAC;YAAC,OAAO,OAAO,EAAE,CAAC;gBACjB,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACjD,6EAA6E;QAC7E,0EAA0E;QAC1E,yEAAyE;QACzE,uEAAuE;QACvE,2EAA2E;QAC3E,4DAA4D;QAC5D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3D,MAAM,KAAK,GAAG,GAAG,YAAY,gBAAgB,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,CAAC;QAClG,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,wEAAwE;YACxE,kEAAkE;YAClE,mEAAmE;YACnE,mEAAmE;YACnE,iEAAiE;YACjE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,qBAAqB,EAAE,OAAO,EAAE,eAAe,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QACjH,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,qCAAqC,EAAE,KAAK,EAAE,CAAC;IACnH,CAAC;IACD,MAAM,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACtB,CAAC","sourcesContent":["/**\n * The journaled commit itself: journal (fsynced) → swap → renames → clear,\n * with archive's ordering on the failure path — the rollback decides the\n * outcome, and only then does the journal have nothing left to describe. A\n * crash anywhere inside leaves either the journal plus a recoverable tree\n * (roll forward finishes it) or a fully rolled-back tree whose stale journal\n * the next recovery reads as the consistent pre-state.\n *\n * Callers stage for themselves before committing, on purpose: vouch's\n * byte-level raced check and gherkin's planned-vs-read compare both have to\n * run BETWEEN staging and the swap, against the pre-images staging read.\n *\n * The DIRECTORY RENAMES (`spec.moves`, `subsystem move`'s half) come after\n * every file swap, deliberately: the generated views file must land in the\n * same commit as the renames or the tree is left failing between two states,\n * and a failure mid-renames still has the swapped files' pre-images in hand\n * to roll everything back together — renames undone newest-first, then the\n * files, one failure list, one verdict.\n */\nimport { rename } from \"node:fs/promises\";\nimport { message, rollbackMessage, rollbackStaged, StagingRaceError, swapStaged } from \"../commit.js\";\nimport { type StagedWrite } from \"../writes.js\";\nimport { clearTxnIntent, writeTxnIntent, type TxnSpec } from \"./journal.js\";\n\nexport type StagedCommit =\n | { ok: true }\n | {\n ok: false;\n code: \"merge-failed\" | \"rollback-incomplete\";\n message: string;\n /**\n * The failure was another writer winning a race (an exclusive create\n * losing to a concurrent one, or the pre-image compare firing), so the\n * caller may map it to its own stable race code — `new` answers\n * `already-exists`, vouch answers `vouch-raced` — instead of the\n * generic merge failure.\n */\n raced: boolean;\n };\n\n/**\n * `what` is the verb the rollback sentence uses for the half-state (\"merged\",\n * \"stamped\", \"written\") — each caller keeps the word its operators already\n * grep their logs for.\n */\nexport async function commitStaged(spec: TxnSpec, staged: StagedWrite[], what: string): Promise<StagedCommit> {\n await writeTxnIntent(spec, staged);\n const performed: { from: string; to: string }[] = [];\n try {\n await swapStaged(staged);\n for (const move of spec.moves ?? []) {\n await rename(move.from, move.to);\n performed.push(move);\n }\n } catch (err) {\n // Renames back first, newest-first: each is its own inverse, and a\n // directory that cannot go back is exactly as reportable as a file that\n // cannot — it joins the same failure list and forces the same verdict.\n const failures: string[] = [];\n for (const move of [...performed].reverse()) {\n try {\n await rename(move.to, move.from);\n } catch (undoErr) {\n failures.push(`${move.to} (${message(undoErr)})`);\n }\n }\n failures.push(...(await rollbackStaged(staged)));\n // The journal is cleared only when the rollback HELD. On rollback-incomplete\n // the tree is genuinely half-written and the temps are gone — the journal\n // is the one durable record of which files, so doctor keeps naming them,\n // validate keeps refusing to grade the tree green, and the next writer\n // refuses commit-interrupted instead of writing over the wreckage. Archive\n // pays the same way by retaining its snapshot on this path.\n if (failures.length === 0) await clearTxnIntent(spec.root);\n const raced = err instanceof StagingRaceError || (err as NodeJS.ErrnoException).code === \"EEXIST\";\n if (failures.length > 0) {\n // NEVER raced, whatever threw: callers remap a raced failure onto their\n // own softer code (vouch-raced, already-exists) with \"nothing was\n // written\" prose — and here something WAS written and could not be\n // taken back. rollback-incomplete is the one answer that demands a\n // human, and no remap may swallow the file list that says where.\n return { ok: false, code: \"rollback-incomplete\", message: rollbackMessage(err, failures, what), raced: false };\n }\n return { ok: false, code: \"merge-failed\", message: `${message(err)} — rolled back, nothing was written`, raced };\n }\n await clearTxnIntent(spec.root);\n return { ok: true };\n}\n"]}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * What a write IS before anything touches disk: the planned record, the staged
3
+ * record, and the bytes both are made of.
4
+ *
5
+ * This is the package's floor. Everything else here — the lock, the swap, the
6
+ * snapshot, the recovery journal — is a rule about WHEN bytes may land; only
7
+ * this module decides what they are. Decoding lives here for the same reason:
8
+ * text is produced only where a parser needs it and `readUtf8` is the one door,
9
+ * so no later addition can reach a docs file through `readFile(…, "utf8")` and
10
+ * silently substitute U+FFFD for bytes loam did not understand.
11
+ */
12
+ import { isUtf8 } from "node:buffer";
13
+ import { createHash } from "node:crypto";
14
+ import { existsSync } from "node:fs";
15
+ import { readFile } from "node:fs/promises";
16
+ /** The one place a planned write becomes bytes. */
17
+ export function toBytes(content) {
18
+ return typeof content === "string" ? Buffer.from(content, "utf8") : content;
19
+ }
20
+ /**
21
+ * The one place that decides whether a planned write is a CREATE *from the
22
+ * filesystem*. (`core/verify/store/commit.ts` is the deliberate second rule:
23
+ * it decides from the caller's held pre-image instead, because a CAS must
24
+ * follow the read it promised, not what `existsSync` says now.)
25
+ *
26
+ * `exclusive` is a no-clobber create (link(2)), so it can only be asked of a
27
+ * path that does not exist yet: asking it of an overwrite would fail EEXIST on
28
+ * every single run. Deciding here — rather than at each of the dozen
29
+ * `writes.push` sites in archive and unarchive — is what keeps the two commands
30
+ * from disagreeing about which of their writes may safely lose a race. An
31
+ * OVERWRITE is not left unguarded: `swapStaged` compares the target against the
32
+ * pre-image immediately before the rename, so a file another writer changed
33
+ * under us stops the commit instead of silently burying their bytes.
34
+ */
35
+ export function planWrite(path, content) {
36
+ if (content !== null && !existsSync(path))
37
+ return { path, content, exclusive: true };
38
+ return { path, content };
39
+ }
40
+ /**
41
+ * A file loam must PARSE that does not decode as the UTF-8 text loam owns.
42
+ *
43
+ * Commands map this to `merge-failed`: nothing was written, the file is named,
44
+ * and re-saving it as UTF-8 makes the command work. The read-only migration
45
+ * ingest already refuses non-UTF-8 input outright (core/openspec/) —
46
+ * the destructive path has strictly more to lose, so it refuses the same way
47
+ * rather than substituting U+FFFD and rewriting the file over the author.
48
+ *
49
+ * `reason` says what the bytes actually ARE, because the two ways a document
50
+ * fails this test destroy different things: one substitutes bytes, the other
51
+ * erases the whole document's meaning. A reader told the wrong one goes looking
52
+ * in the wrong place. The advice that follows is the same either way, so it is
53
+ * spelled once, here.
54
+ */
55
+ export class NotUtf8Error extends Error {
56
+ path;
57
+ name = "NotUtf8Error";
58
+ constructor(path, reason) {
59
+ super(`${path} ${reason} Nothing was written. Re-save it as UTF-8, then re-run.`);
60
+ this.path = path;
61
+ }
62
+ }
63
+ /**
64
+ * Decode bytes a parser is about to be handed, or refuse naming the file.
65
+ *
66
+ * Two tests, the same two the read path makes (document-bytes.ts's
67
+ * `decodeDocument`), because this side has strictly more to lose by missing
68
+ * either. `isUtf8` catches the byte sequences that cannot be UTF-8 at all —
69
+ * which is what a byte-order mark makes UTF-16 into. Without a BOM, UTF-16LE of
70
+ * ASCII is a sequence of perfectly VALID UTF-8 bytes: every other byte is NUL,
71
+ * so `isUtf8` says yes, the file decodes with a U+0000 between every character,
72
+ * and every parser loam owns reads its requirements, frontmatter and headings
73
+ * as absent rather than as unreadable. On the read path that is a document
74
+ * graded green while nothing in it was checked; here it is worse, because
75
+ * `archive` merges against that empty baseline and writes the result back — the
76
+ * author's requirements are not mis-read, they are gone, and the undo snapshot
77
+ * records the same emptiness as the pre-image.
78
+ */
79
+ function decodeUtf8(bytes, path) {
80
+ if (!isUtf8(bytes)) {
81
+ throw new NotUtf8Error(path, `is not valid UTF-8 — loam parses this file as text and writes it back, which would replace every ` +
82
+ `undecodable byte with U+FFFD, in the living document AND in the undo snapshot.`);
83
+ }
84
+ const text = bytes.toString("utf8");
85
+ if (text.includes("\u0000")) {
86
+ throw new NotUtf8Error(path, `holds NUL characters, which no document loam owns does — almost always UTF-16 saved without a byte-order ` +
87
+ `mark. Its bytes are valid UTF-8, so it decodes without complaint into text whose requirements, ` +
88
+ `frontmatter and headings all parse as ABSENT: the merge would compute over an empty baseline and write ` +
89
+ `that back, taking every requirement in the file with it.`);
90
+ }
91
+ return text;
92
+ }
93
+ /** Read a file loam is going to parse. The only supported way to turn a docs file into a string. */
94
+ export async function readUtf8(path) {
95
+ return decodeUtf8(await readFile(path), path);
96
+ }
97
+ /** Same bytes? Absence is its own value: a file that is gone is not a file that is empty. */
98
+ export function sameBytes(a, b) {
99
+ return a === null || b === null ? a === b : a.equals(b);
100
+ }
101
+ /* ------------------------------------------------------------------ */
102
+ /* Reading loam's own records back off the disk */
103
+ /* ------------------------------------------------------------------ */
104
+ /** The one spelling of a sha256 loam wrote, for reading one back out of a record. */
105
+ export function isDigest(value) {
106
+ return typeof value === "string" && /^[0-9a-f]{64}$/.test(value);
107
+ }
108
+ export function sha256(content) {
109
+ return typeof content === "string"
110
+ ? createHash("sha256").update(content, "utf8").digest("hex")
111
+ : createHash("sha256").update(content).digest("hex");
112
+ }
113
+ //# sourceMappingURL=writes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writes.js","sourceRoot":"","sources":["../../../src/core/staging/writes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AA8B5C,mDAAmD;AACnD,MAAM,UAAU,OAAO,CAAC,OAA+B;IACrD,OAAO,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;AAC9E,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,OAA+B;IACrE,IAAI,OAAO,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACrF,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC3B,CAAC;AAsBD;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;IAI1B,IAAI;IAHG,IAAI,GAAG,cAAc,CAAC;IAExC,YACW,IAAY,EACrB,MAAc;QAEd,KAAK,CAAC,GAAG,IAAI,IAAI,MAAM,yDAAyD,CAAC,CAAC;oBAHzE,IAAI;IAIf,CAAC;CACF;AAED;;;;;;;;;;;;;;;GAeG;AACH,SAAS,UAAU,CAAC,KAAa,EAAE,IAAY;IAC7C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,YAAY,CACpB,IAAI,EACJ,mGAAmG;YACjG,gFAAgF,CACnF,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,YAAY,CACpB,IAAI,EACJ,2GAA2G;YACzG,iGAAiG;YACjG,yGAAyG;YACzG,0DAA0D,CAC7D,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,oGAAoG;AACpG,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAY;IACzC,OAAO,UAAU,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;AAChD,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,SAAS,CAAC,CAAgB,EAAE,CAAgB;IAC1D,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,wEAAwE;AACxE,yEAAyE;AACzE,wEAAwE;AAExE,qFAAqF;AACrF,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,OAAwB;IAC7C,OAAO,OAAO,OAAO,KAAK,QAAQ;QAChC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QAC5D,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACzD,CAAC","sourcesContent":["/**\n * What a write IS before anything touches disk: the planned record, the staged\n * record, and the bytes both are made of.\n *\n * This is the package's floor. Everything else here — the lock, the swap, the\n * snapshot, the recovery journal — is a rule about WHEN bytes may land; only\n * this module decides what they are. Decoding lives here for the same reason:\n * text is produced only where a parser needs it and `readUtf8` is the one door,\n * so no later addition can reach a docs file through `readFile(…, \"utf8\")` and\n * silently substitute U+FFFD for bytes loam did not understand.\n */\nimport { isUtf8 } from \"node:buffer\";\nimport { createHash } from \"node:crypto\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\n\n/**\n * A planned file write — the merge is computed fully before anything touches\n * disk. `content: null` means \"delete this file\"; only `unarchive` plans those,\n * to take back a file the archive created.\n *\n * BYTES, not text. The whole write path — pre-image, staged content, snapshot,\n * the compare-and-set before each swap — moves Buffers, because a docs repo is\n * not guaranteed to hold text loam can decode: `Buffer.toString(\"utf8\")` never\n * fails, it substitutes U+FFFD, so reading a file as a string and writing the\n * string back is a silent, permanent rewrite of every byte loam did not\n * understand. It reached the snapshot too, so `unarchive` restored the damage\n * and reported success. Text is now produced only where a PARSER needs it, by\n * `decodeUtf8`, which refuses instead of substituting.\n */\nexport interface PlannedWrite {\n path: string;\n /**\n * A `string` is accepted for the callers whose new version IS text they\n * composed from nothing (a re-pinned delta, a restamped spec) and is encoded\n * as UTF-8 once, at staging. A caller holding BYTES — anything derived from a\n * file loam read — must hand over the Buffer: the round trip through a string\n * is the corruption above.\n */\n content: string | Buffer | null;\n /** Create the target atomically without replacing a concurrently created file. */\n exclusive?: boolean;\n}\n\n/** The one place a planned write becomes bytes. */\nexport function toBytes(content: string | Buffer | null): Buffer | null {\n return typeof content === \"string\" ? Buffer.from(content, \"utf8\") : content;\n}\n\n/**\n * The one place that decides whether a planned write is a CREATE *from the\n * filesystem*. (`core/verify/store/commit.ts` is the deliberate second rule:\n * it decides from the caller's held pre-image instead, because a CAS must\n * follow the read it promised, not what `existsSync` says now.)\n *\n * `exclusive` is a no-clobber create (link(2)), so it can only be asked of a\n * path that does not exist yet: asking it of an overwrite would fail EEXIST on\n * every single run. Deciding here — rather than at each of the dozen\n * `writes.push` sites in archive and unarchive — is what keeps the two commands\n * from disagreeing about which of their writes may safely lose a race. An\n * OVERWRITE is not left unguarded: `swapStaged` compares the target against the\n * pre-image immediately before the rename, so a file another writer changed\n * under us stops the commit instead of silently burying their bytes.\n */\nexport function planWrite(path: string, content: string | Buffer | null): PlannedWrite {\n if (content !== null && !existsSync(path)) return { path, content, exclusive: true };\n return { path, content };\n}\n\n/** A planned write with its new bytes parked next to the target, ready to swap in. */\nexport interface StagedWrite {\n write: PlannedWrite;\n /** What this write puts on disk — `write.content` encoded once. Null for a delete. */\n content: Buffer | null;\n /** Temp file holding the new bytes, in the target's OWN directory. Null for a delete. */\n tmp: string | null;\n /** The target's bytes before the swap; null when it did not exist. */\n before: Buffer | null;\n /**\n * Topmost directory this write had to create, if any — a rollback owes the repo\n * its directories back too. An empty `services/<svc>/` left standing is the\n * fleet claiming a service that was never merged.\n */\n createdDir: string | null;\n swapped: boolean;\n}\n\n\n\n/**\n * A file loam must PARSE that does not decode as the UTF-8 text loam owns.\n *\n * Commands map this to `merge-failed`: nothing was written, the file is named,\n * and re-saving it as UTF-8 makes the command work. The read-only migration\n * ingest already refuses non-UTF-8 input outright (core/openspec/) —\n * the destructive path has strictly more to lose, so it refuses the same way\n * rather than substituting U+FFFD and rewriting the file over the author.\n *\n * `reason` says what the bytes actually ARE, because the two ways a document\n * fails this test destroy different things: one substitutes bytes, the other\n * erases the whole document's meaning. A reader told the wrong one goes looking\n * in the wrong place. The advice that follows is the same either way, so it is\n * spelled once, here.\n */\nexport class NotUtf8Error extends Error {\n override readonly name = \"NotUtf8Error\";\n\n constructor(\n readonly path: string,\n reason: string,\n ) {\n super(`${path} ${reason} Nothing was written. Re-save it as UTF-8, then re-run.`);\n }\n}\n\n/**\n * Decode bytes a parser is about to be handed, or refuse naming the file.\n *\n * Two tests, the same two the read path makes (document-bytes.ts's\n * `decodeDocument`), because this side has strictly more to lose by missing\n * either. `isUtf8` catches the byte sequences that cannot be UTF-8 at all —\n * which is what a byte-order mark makes UTF-16 into. Without a BOM, UTF-16LE of\n * ASCII is a sequence of perfectly VALID UTF-8 bytes: every other byte is NUL,\n * so `isUtf8` says yes, the file decodes with a U+0000 between every character,\n * and every parser loam owns reads its requirements, frontmatter and headings\n * as absent rather than as unreadable. On the read path that is a document\n * graded green while nothing in it was checked; here it is worse, because\n * `archive` merges against that empty baseline and writes the result back — the\n * author's requirements are not mis-read, they are gone, and the undo snapshot\n * records the same emptiness as the pre-image.\n */\nfunction decodeUtf8(bytes: Buffer, path: string): string {\n if (!isUtf8(bytes)) {\n throw new NotUtf8Error(\n path,\n `is not valid UTF-8 — loam parses this file as text and writes it back, which would replace every ` +\n `undecodable byte with U+FFFD, in the living document AND in the undo snapshot.`,\n );\n }\n const text = bytes.toString(\"utf8\");\n if (text.includes(\"\\u0000\")) {\n throw new NotUtf8Error(\n path,\n `holds NUL characters, which no document loam owns does — almost always UTF-16 saved without a byte-order ` +\n `mark. Its bytes are valid UTF-8, so it decodes without complaint into text whose requirements, ` +\n `frontmatter and headings all parse as ABSENT: the merge would compute over an empty baseline and write ` +\n `that back, taking every requirement in the file with it.`,\n );\n }\n return text;\n}\n\n/** Read a file loam is going to parse. The only supported way to turn a docs file into a string. */\nexport async function readUtf8(path: string): Promise<string> {\n return decodeUtf8(await readFile(path), path);\n}\n\n/** Same bytes? Absence is its own value: a file that is gone is not a file that is empty. */\nexport function sameBytes(a: Buffer | null, b: Buffer | null): boolean {\n return a === null || b === null ? a === b : a.equals(b);\n}\n\n/* ------------------------------------------------------------------ */\n/* Reading loam's own records back off the disk */\n/* ------------------------------------------------------------------ */\n\n/** The one spelling of a sha256 loam wrote, for reading one back out of a record. */\nexport function isDigest(value: unknown): value is string {\n return typeof value === \"string\" && /^[0-9a-f]{64}$/.test(value);\n}\n\nexport function sha256(content: Buffer | string): string {\n return typeof content === \"string\"\n ? createHash(\"sha256\").update(content, \"utf8\").digest(\"hex\")\n : createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\n"]}
@@ -0,0 +1,49 @@
1
+ const EDIT_STEPS = new Set([
2
+ "next.author-intent",
3
+ "next.touch-service",
4
+ "next.author-spec",
5
+ "next.author-openapi",
6
+ "next.author-scenarios",
7
+ ]);
8
+ const SERVICE_REPO_STEPS = new Set([
9
+ "next.bind-service",
10
+ "next.adopt-first",
11
+ "next.generate-tests",
12
+ "next.verify-unconfirmed",
13
+ "next.attest-service",
14
+ "next.verify-attested",
15
+ ]);
16
+ function placeholders(command) {
17
+ return [...command.matchAll(/<([^>]+)>/g)].map((match) => match[1]);
18
+ }
19
+ function execution(step) {
20
+ if (EDIT_STEPS.has(step.code)) {
21
+ return {
22
+ kind: "edit",
23
+ runnable: false,
24
+ cwd: "docs-repo",
25
+ after: step.command,
26
+ needs: [step.path ?? "the artifact named in statement"],
27
+ };
28
+ }
29
+ if (step.code === "next.recover-commit" && step.command === "loam doctor --json") {
30
+ return {
31
+ kind: "human-review",
32
+ runnable: false,
33
+ cwd: "docs-repo",
34
+ needs: ["compare the living docs with version control"],
35
+ };
36
+ }
37
+ const needs = placeholders(step.command);
38
+ const serviceRepo = SERVICE_REPO_STEPS.has(step.code);
39
+ return {
40
+ kind: serviceRepo ? "external-repo" : "command",
41
+ runnable: needs.length === 0,
42
+ cwd: serviceRepo ? "service-repo" : "configured-repo",
43
+ ...(needs.length === 0 ? { command: step.command } : { needs }),
44
+ };
45
+ }
46
+ export function executableNext(step) {
47
+ return { ...step, execution: execution(step) };
48
+ }
49
+ //# sourceMappingURL=execution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execution.js","sourceRoot":"","sources":["../../../../src/core/status/actions/execution.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC;IACzB,oBAAoB;IACpB,oBAAoB;IACpB,kBAAkB;IAClB,qBAAqB;IACrB,uBAAuB;CACxB,CAAC,CAAC;AAEH,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC;IACjC,mBAAmB;IACnB,kBAAkB;IAClB,qBAAqB;IACrB,yBAAyB;IACzB,qBAAqB;IACrB,sBAAsB;CACvB,CAAC,CAAC;AAEH,SAAS,YAAY,CAAC,OAAe;IACnC,OAAO,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,SAAS,CAAC,IAAc;IAC/B,IAAI,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,OAAO;YACL,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE,KAAK;YACf,GAAG,EAAE,WAAW;YAChB,KAAK,EAAE,IAAI,CAAC,OAAO;YACnB,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,iCAAiC,CAAC;SACxD,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,qBAAqB,IAAI,IAAI,CAAC,OAAO,KAAK,oBAAoB,EAAE,CAAC;QACjF,OAAO;YACL,IAAI,EAAE,cAAc;YACpB,QAAQ,EAAE,KAAK;YACf,GAAG,EAAE,WAAW;YAChB,KAAK,EAAE,CAAC,8CAA8C,CAAC;SACxD,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,WAAW,GAAG,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtD,OAAO;QACL,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS;QAC/C,QAAQ,EAAE,KAAK,CAAC,MAAM,KAAK,CAAC;QAC5B,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,iBAAiB;QACrD,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;KAChE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAc;IAC3C,OAAO,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;AACjD,CAAC","sourcesContent":["/** The executable machine contract layered over status's legacy command hint. */\nimport type {\n ExecutableNextStep,\n NextExecution,\n NextStep,\n} from \"../report.js\";\n\nconst EDIT_STEPS = new Set([\n \"next.author-intent\",\n \"next.touch-service\",\n \"next.author-spec\",\n \"next.author-openapi\",\n \"next.author-scenarios\",\n]);\n\nconst SERVICE_REPO_STEPS = new Set([\n \"next.bind-service\",\n \"next.adopt-first\",\n \"next.generate-tests\",\n \"next.verify-unconfirmed\",\n \"next.attest-service\",\n \"next.verify-attested\",\n]);\n\nfunction placeholders(command: string): string[] {\n return [...command.matchAll(/<([^>]+)>/g)].map((match) => match[1]!);\n}\n\nfunction execution(step: NextStep): NextExecution {\n if (EDIT_STEPS.has(step.code)) {\n return {\n kind: \"edit\",\n runnable: false,\n cwd: \"docs-repo\",\n after: step.command,\n needs: [step.path ?? \"the artifact named in statement\"],\n };\n }\n if (step.code === \"next.recover-commit\" && step.command === \"loam doctor --json\") {\n return {\n kind: \"human-review\",\n runnable: false,\n cwd: \"docs-repo\",\n needs: [\"compare the living docs with version control\"],\n };\n }\n const needs = placeholders(step.command);\n const serviceRepo = SERVICE_REPO_STEPS.has(step.code);\n return {\n kind: serviceRepo ? \"external-repo\" : \"command\",\n runnable: needs.length === 0,\n cwd: serviceRepo ? \"service-repo\" : \"configured-repo\",\n ...(needs.length === 0 ? { command: step.command } : { needs }),\n };\n}\n\nexport function executableNext(step: NextStep): ExecutableNextStep {\n return { ...step, execution: execution(step) };\n}\n"]}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Who owes a service its `openapi.yaml`, and which features in flight already
3
+ * carry one. Shared between the artifact table and the fleet form, because the
4
+ * two must never disagree about whether a feature owes a contract.
5
+ */
6
+ import { existsSync } from "node:fs";
7
+ import { featureSpecPaths } from "../repo/paths.js";
8
+ import { listFeatures } from "../repo/repo.js";
9
+ /**
10
+ * Does this feature owe `svc` an openapi.yaml?
11
+ *
12
+ * Keyed on the living CONTRACT FILE, never on `services/<svc>/` merely existing
13
+ * as a directory. A service adopted without a contract is precisely the case
14
+ * where a feature that governs operations has to write one, and the directory
15
+ * test answered "none owed" there — printing `(not written — none owed)` beside
16
+ * the `spec-api.op-undefined` finding, in the same payload, about that very
17
+ * file, while suppressing the only step that names it.
18
+ *
19
+ * Two ways to owe nothing, both about somebody else discharging it: the living
20
+ * docs already carry the contract, or another feature in flight brings it — the
21
+ * same softening coherence applies with its `*-pending` codes, where an
22
+ * operation "defined by another feature still in flight" is an ordering fact
23
+ * and not a hole. And one way to owe nothing at all: a service that ALREADY
24
+ * EXISTS under `services/`, has no living contract, and that this feature sends
25
+ * no operation to. A UI or a worker nobody calls has no API to write down,
26
+ * which is the reading `validate` and `list` already take — demanding one from
27
+ * an adopted service of that shape would be loam telling an author to invent a
28
+ * contract.
29
+ *
30
+ * That last exemption is deliberately withheld from a service the living docs
31
+ * have never heard of. A feature that introduces a service and sends it no
32
+ * operation still owes the file, because nothing else in the repository will
33
+ * ever describe its surface, and `c4-api.op-undefined` fires on an op-tagged
34
+ * delta edge whether or not any `Operations:` line was written — so answering
35
+ * "none owed" there printed `(not written — none owed)` in the same payload as
36
+ * an error about that very file.
37
+ */
38
+ export function owesContract(entry, contracted, governsOperations) {
39
+ // Both living facts come off the ENUMERATION's entry rather than root
40
+ // existsSync probes: a service filed into a subsystem lives wherever the
41
+ // tree walk found it, so the root probe read every filed service as "living
42
+ // docs have never heard of it" and demanded a contract from all of them.
43
+ // `undefined` IS that never-heard-of case — nothing enumerated the id
44
+ // anywhere. `contracted` arrives resolved by the caller, and deliberately
45
+ // not through the entry: another feature in flight discharges the debt for
46
+ // a service the living docs have NOT heard of yet — that is the ordinary
47
+ // introduce-then-build ordering, and it must keep answering "none owed".
48
+ if (entry?.has.openapi === true)
49
+ return false;
50
+ if (contracted)
51
+ return false;
52
+ return entry === undefined || governsOperations;
53
+ }
54
+ /**
55
+ * Which features in flight carry an `openapi.yaml` for which service — one
56
+ * enumeration of the whole index, service by service.
57
+ *
58
+ * The owners are recorded as feature IDS rather than directories because that
59
+ * is the comparison {@link contractsHeldElsewhere} makes: two directories that
60
+ * spell the same id are one feature as far as this question goes, and an index
61
+ * keyed by directory would let a duplicated id answer "somebody else has it"
62
+ * about its own twin.
63
+ *
64
+ * Enumeration only — the feature list and its `specs/` subdirectories are
65
+ * already in the request-scoped index by the time anything asks.
66
+ */
67
+ export async function contractOwners(docsDir, context) {
68
+ const out = new Map();
69
+ for (const f of await listFeatures(docsDir, {}, context)) {
70
+ for (const svc of f.services) {
71
+ if (!existsSync(featureSpecPaths(f.dir, svc).openapi))
72
+ continue;
73
+ const owners = out.get(svc);
74
+ if (owners === undefined)
75
+ out.set(svc, new Set([f.id]));
76
+ else
77
+ owners.add(f.id);
78
+ }
79
+ }
80
+ return out;
81
+ }
82
+ /**
83
+ * Services some OTHER active feature already carries an `openapi.yaml` for.
84
+ *
85
+ * Derived from the shared index rather than re-walked, because the answer for
86
+ * feature A and the answer for feature B are two readings of one enumeration:
87
+ * asking it once per feature made the fleet form pay F²·S `existsSync` calls
88
+ * over the two quantities that actually grow.
89
+ */
90
+ export function contractsHeldElsewhere(owners, self) {
91
+ const out = new Set();
92
+ for (const [svc, ids] of owners) {
93
+ for (const id of ids) {
94
+ if (id !== self) {
95
+ out.add(svc);
96
+ break;
97
+ }
98
+ }
99
+ }
100
+ return out;
101
+ }
102
+ //# sourceMappingURL=contracts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contracts.js","sourceRoot":"","sources":["../../../src/core/status/contracts.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,YAAY,CAC1B,KAA+B,EAC/B,UAAmB,EACnB,iBAA0B;IAE1B,sEAAsE;IACtE,yEAAyE;IACzE,4EAA4E;IAC5E,yEAAyE;IACzE,sEAAsE;IACtE,0EAA0E;IAC1E,2EAA2E;IAC3E,yEAAyE;IACzE,yEAAyE;IACzE,IAAI,KAAK,EAAE,GAAG,CAAC,OAAO,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9C,IAAI,UAAU;QAAE,OAAO,KAAK,CAAC;IAC7B,OAAO,KAAK,KAAK,SAAS,IAAI,iBAAiB,CAAC;AAClD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAgB,EAAE,OAAqB;IAC1E,MAAM,GAAG,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC3C,KAAK,MAAM,CAAC,IAAI,MAAM,YAAY,CAAC,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC;QACzD,KAAK,MAAM,GAAG,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC7B,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC;gBAAE,SAAS;YAChE,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,MAAM,KAAK,SAAS;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;;gBACnD,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAgD,EAAE,IAAY;IACnG,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,EAAE,CAAC;QAChC,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;YACrB,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;gBAChB,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACb,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["/**\n * Who owes a service its `openapi.yaml`, and which features in flight already\n * carry one. Shared between the artifact table and the fleet form, because the\n * two must never disagree about whether a feature owes a contract.\n */\nimport { existsSync } from \"node:fs\";\nimport type { FleetContext } from \"../fleet-context.js\";\nimport { featureSpecPaths } from \"../repo/paths.js\";\nimport type { ServiceEntry } from \"../repo/entries.js\";\nimport { listFeatures } from \"../repo/repo.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\n\n/**\n * Does this feature owe `svc` an openapi.yaml?\n *\n * Keyed on the living CONTRACT FILE, never on `services/<svc>/` merely existing\n * as a directory. A service adopted without a contract is precisely the case\n * where a feature that governs operations has to write one, and the directory\n * test answered \"none owed\" there — printing `(not written — none owed)` beside\n * the `spec-api.op-undefined` finding, in the same payload, about that very\n * file, while suppressing the only step that names it.\n *\n * Two ways to owe nothing, both about somebody else discharging it: the living\n * docs already carry the contract, or another feature in flight brings it — the\n * same softening coherence applies with its `*-pending` codes, where an\n * operation \"defined by another feature still in flight\" is an ordering fact\n * and not a hole. And one way to owe nothing at all: a service that ALREADY\n * EXISTS under `services/`, has no living contract, and that this feature sends\n * no operation to. A UI or a worker nobody calls has no API to write down,\n * which is the reading `validate` and `list` already take — demanding one from\n * an adopted service of that shape would be loam telling an author to invent a\n * contract.\n *\n * That last exemption is deliberately withheld from a service the living docs\n * have never heard of. A feature that introduces a service and sends it no\n * operation still owes the file, because nothing else in the repository will\n * ever describe its surface, and `c4-api.op-undefined` fires on an op-tagged\n * delta edge whether or not any `Operations:` line was written — so answering\n * \"none owed\" there printed `(not written — none owed)` in the same payload as\n * an error about that very file.\n */\nexport function owesContract(\n entry: ServiceEntry | undefined,\n contracted: boolean,\n governsOperations: boolean,\n): boolean {\n // Both living facts come off the ENUMERATION's entry rather than root\n // existsSync probes: a service filed into a subsystem lives wherever the\n // tree walk found it, so the root probe read every filed service as \"living\n // docs have never heard of it\" and demanded a contract from all of them.\n // `undefined` IS that never-heard-of case — nothing enumerated the id\n // anywhere. `contracted` arrives resolved by the caller, and deliberately\n // not through the entry: another feature in flight discharges the debt for\n // a service the living docs have NOT heard of yet — that is the ordinary\n // introduce-then-build ordering, and it must keep answering \"none owed\".\n if (entry?.has.openapi === true) return false;\n if (contracted) return false;\n return entry === undefined || governsOperations;\n}\n\n/**\n * Which features in flight carry an `openapi.yaml` for which service — one\n * enumeration of the whole index, service by service.\n *\n * The owners are recorded as feature IDS rather than directories because that\n * is the comparison {@link contractsHeldElsewhere} makes: two directories that\n * spell the same id are one feature as far as this question goes, and an index\n * keyed by directory would let a duplicated id answer \"somebody else has it\"\n * about its own twin.\n *\n * Enumeration only — the feature list and its `specs/` subdirectories are\n * already in the request-scoped index by the time anything asks.\n */\nexport async function contractOwners(docsDir: DocsDir, context: FleetContext): Promise<Map<string, Set<string>>> {\n const out = new Map<string, Set<string>>();\n for (const f of await listFeatures(docsDir, {}, context)) {\n for (const svc of f.services) {\n if (!existsSync(featureSpecPaths(f.dir, svc).openapi)) continue;\n const owners = out.get(svc);\n if (owners === undefined) out.set(svc, new Set([f.id]));\n else owners.add(f.id);\n }\n }\n return out;\n}\n\n/**\n * Services some OTHER active feature already carries an `openapi.yaml` for.\n *\n * Derived from the shared index rather than re-walked, because the answer for\n * feature A and the answer for feature B are two readings of one enumeration:\n * asking it once per feature made the fleet form pay F²·S `existsSync` calls\n * over the two quantities that actually grow.\n */\nexport function contractsHeldElsewhere(owners: ReadonlyMap<string, ReadonlySet<string>>, self: string): Set<string> {\n const out = new Set<string>();\n for (const [svc, ids] of owners) {\n for (const id of ids) {\n if (id !== self) {\n out.add(svc);\n break;\n }\n }\n }\n return out;\n}\n"]}
@@ -0,0 +1,196 @@
1
+ /**
2
+ * The artifact table: which files a feature owes, whether each is on disk, and
3
+ * which finding turns a row `draft`. Nothing is graded here — the shared
4
+ * checks already ran, and this module only attributes what they reported to
5
+ * the file a reader should edit.
6
+ */
7
+ import { existsSync } from "node:fs";
8
+ import { repoPath } from "../../envelope/json.js";
9
+ import { featurePaths, featureSpecPaths } from "../../repo/paths.js";
10
+ import { verificationPath } from "../../verify/record.js";
11
+ import { owesContract } from "../contracts.js";
12
+ import { verificationStatus } from "../verification.js";
13
+ /**
14
+ * The `capability.*` codes filed against a capability DOCUMENT rather than
15
+ * against a service requirement's join line. Spelled out rather than matched by
16
+ * prefix because the family straddles both corpora, and the whole point of this
17
+ * table is to name one file.
18
+ */
19
+ const CAPABILITY_DOCUMENT_CODES = new Set([
20
+ "capability.requirement-unidentified",
21
+ "capability.requirement-service-scoped",
22
+ "capability.requirement-inert-join",
23
+ "capability.uncovered",
24
+ "capability.remove-requirement-realized",
25
+ ]);
26
+ /**
27
+ * Which artifact an error names.
28
+ *
29
+ * Attribution is by code family, because that is the only machine-readable
30
+ * pointer an `Issue` carries: `subject` narrows to a NAME, never to a file.
31
+ * The families are unambiguous — `c4-api.*`/`c4-event.*`/`c4.*` and the two
32
+ * `delta.*` codes about the LikeC4 document itself grade the architecture
33
+ * axis, `openapi.*` grades the API contract, `asyncapi.*` the event contract,
34
+ * and the remaining `delta.*` codes plus `spec-api.*`/`spec-event.*` are the
35
+ * delta-shape checks, every one of which is about a requirement in a spec
36
+ * delta.
37
+ *
38
+ * An error that maps to nothing (or whose subject is unknown) still lands in
39
+ * `checks.issues` and still drives `next.fix-coherence` — it just does not turn
40
+ * any single artifact `draft`, because guessing which file to blame is how a
41
+ * reader gets sent to edit the wrong one.
42
+ *
43
+ * THE BUSINESS CORPUS IS THE ONE PLACE THE CODE FAMILY IS NOT ENOUGH. A
44
+ * `delta.*` issue's `subject` is a service id OR a capability id
45
+ * (`core/delta/scope.ts`), and so is a `scaffold.placeholder`'s
46
+ * (`core/coherence/authoring/scaffold.ts` files one per unedited capability
47
+ * delta), and nothing in either code says which — so this
48
+ * function answers `spec` for both and `featureArtifacts` below resolves it by
49
+ * MEMBERSHIP, against the two lists it already holds. Where a name is only a
50
+ * capability the capability row is turned `draft` and no service row is; where
51
+ * a fleet holds a service AND a capability of the same name, BOTH are, which is
52
+ * the pessimistic answer this module is required to give and strictly better
53
+ * than the old one (it named the service's spec delta and was silently wrong).
54
+ * The capability-document codes below need no such resolution: they are only
55
+ * ever filed against a capability.
56
+ */
57
+ function faultedArtifact(code, subject) {
58
+ if (code === "delta.invalid" || code === "delta.nothing-tagged")
59
+ return "delta";
60
+ if (code.startsWith("c4-api.") || code.startsWith("c4-event.") || code.startsWith("c4."))
61
+ return "delta";
62
+ if (code.startsWith("openapi."))
63
+ return "openapi";
64
+ if (code.startsWith("asyncapi."))
65
+ return "asyncapi";
66
+ // The capability document's own grades, and the two halves of the `Realizes:`
67
+ // join a feature can break — every one of them names a
68
+ // `features/<FEAT>/capabilities/<id>/spec.md`, never a service delta. The two
69
+ // `capability.*` codes deliberately absent are `capability.unknown` and
70
+ // `capability.realizes-unknown`: those grade a SERVICE requirement's join
71
+ // lines and carry a service id, so the prefix alone would send their reader
72
+ // to the wrong file.
73
+ if (CAPABILITY_DOCUMENT_CODES.has(code))
74
+ return "capabilities";
75
+ if (code.startsWith("delta.") || code.startsWith("spec-api.") || code.startsWith("spec-event."))
76
+ return "spec";
77
+ // The two families validate contributes: the frontmatter checks read only
78
+ // intent.md, and a requirement with no scenario is a requirement in a spec
79
+ // delta. Both name exactly one file, so both may turn a row `draft`.
80
+ if (code.startsWith("frontmatter."))
81
+ return "intent";
82
+ if (code === "requirements.missing-scenarios")
83
+ return "spec";
84
+ // The authoring gate names exactly one file per finding too — leaving these
85
+ // unmapped had the table calling intent.md `done` ("nothing is owed here")
86
+ // while the archive exited 1 because it says nothing. A placeholder finding
87
+ // carries no subject when it is the C4 delta's scaffolded description; with a
88
+ // subject it is a SPEC DELTA IN EITHER CORPUS — a service id when the text
89
+ // sits in `specs/<svc>/`, a capability id when it sits in
90
+ // `capabilities/<id>/` — which is why `spec` here is resolved by membership
91
+ // below and must not be narrowed to the service list.
92
+ if (code === "intent.empty")
93
+ return "intent";
94
+ if (code === "scaffold.placeholder")
95
+ return subject === undefined ? "delta" : "spec";
96
+ return null;
97
+ }
98
+ export function featureArtifacts(docsDir, feature, services, grading) {
99
+ const { blocking, verification, contracted, governs, living, capabilities } = grading;
100
+ const paths = featurePaths(feature.dir);
101
+ const rel = (abs) => repoPath(docsDir, abs);
102
+ const faults = blocking.map((f) => ({ artifact: faultedArtifact(f.code, f.subject), subject: f.subject }));
103
+ const faulted = (id, service) => faults.some((f) => f.artifact === id && (service === null || f.subject === service));
104
+ const out = [
105
+ fileState("intent", { service: null, path: rel(paths.intent), exists: existsSync(paths.intent) }, true, faulted("intent", null)),
106
+ fileState("delta", { service: null, path: rel(paths.delta), exists: existsSync(paths.delta) }, false, faulted("delta", null)),
107
+ ];
108
+ // One row per capability document the feature carries, and none at all when
109
+ // it carries none: the row is the answer to "which file do I edit", and a
110
+ // fleet that has not adopted the axis has no such file to name. Never
111
+ // `required` — no feature owes a business change — so absence never shows up
112
+ // as work owed, and only a fault turns one `draft`. A `delta.*` fault carries
113
+ // the capability id in `subject` with nothing saying it is not a service, so
114
+ // the membership test is the resolution the code family cannot give.
115
+ for (const doc of capabilities) {
116
+ out.push({
117
+ id: "capabilities",
118
+ service: null,
119
+ capability: doc.id,
120
+ path: rel(doc.spec),
121
+ exists: existsSync(doc.spec),
122
+ required: false,
123
+ status: faulted("capabilities", doc.id) || faulted("spec", doc.id) ? "draft" : "done",
124
+ blockedBy: [],
125
+ });
126
+ }
127
+ for (const svc of services) {
128
+ const p = featureSpecPaths(feature.dir, svc);
129
+ const specFault = faulted("spec", svc);
130
+ // What a service delta owes is a REQUIREMENT DELTA, not specifically the
131
+ // business one. `arch.spec.md` is the other corpus, with the same grammar
132
+ // and the same delta algebra, and `featureChecklist` derives claims from
133
+ // it — so a directory holding only the architectural document is a
134
+ // complete delta, and every gate already reads it as one: `coherence`'s
135
+ // `service.no-requirement-delta` counts the DIRECTORY, and
136
+ // `validate --feature` grades an arch-only delta clean at exit 0.
137
+ //
138
+ // Requiring `spec.md` unconditionally made this module the one document
139
+ // that disagreed. An architecture-only feature — an outbox added, a retry
140
+ // policy, an alert; the changes `arch.spec.md` exists for because no
141
+ // business scenario was ever going to carry them — reported `missing` and
142
+ // emitted `next.author-spec`, sending the author to write business
143
+ // requirements the change does not have, about a feature the gate accepts.
144
+ // status may be redder than the gates on a question they also ask; it may
145
+ // not invent an obligation they do not.
146
+ const archExists = existsSync(p.archSpec);
147
+ out.push(fileState("spec", { service: svc, path: rel(p.spec), exists: existsSync(p.spec) }, !archExists, specFault));
148
+ out.push(fileState("arch-spec", { service: svc, path: rel(p.archSpec), exists: archExists }, false, specFault));
149
+ out.push(fileState("openapi", { service: svc, path: rel(p.openapi), exists: existsSync(p.openapi) }, owesContract(living.get(svc), contracted.has(svc), governs.has(svc)), faulted("openapi", svc)));
150
+ // Never required: an event contract is genuinely optional — the axis's own
151
+ // absence-grading rests on that — so absence is `done`, exactly like
152
+ // arch.spec.md. Present and faulted still turns the row `draft`.
153
+ out.push(fileState("asyncapi", { service: svc, path: rel(p.asyncapi), exists: existsSync(p.asyncapi) }, false, faulted("asyncapi", svc)));
154
+ }
155
+ // The record is the one artifact with a real prerequisite: its checklist is
156
+ // derived from the delta and the per-service deltas, so with none of them
157
+ // written there is nothing to answer — authoring one is impossible rather
158
+ // than merely premature, which is the whole distinction `blocked` carries.
159
+ //
160
+ // `capabilities` is excluded for exactly that reason and not by oversight:
161
+ // `featureChecklist` derives no claim from a capability document — a business
162
+ // promise is realized by service requirements, and those are what the record
163
+ // answers — so counting one as a feeder would report a capability-only
164
+ // feature as ready to verify against a checklist with nothing in it.
165
+ //
166
+ // `arch-spec` used to be excluded beside it, and that failed the same test
167
+ // the comment above states: `featureChecklist` DOES read `arch.spec.md`
168
+ // (`core/verify/checklist.ts`), and `loam verify` on an architecture-only
169
+ // feature returns a real checklist. The row said `blocked … needs delta,
170
+ // spec, openapi, asyncapi` and `next.verify` said there was nothing to derive
171
+ // a checklist from, about a command that derives one — which is the failure
172
+ // `blocked` is supposed to prevent, pointed the wrong way.
173
+ const feeders = out.filter((a) => a.id !== "intent" && a.id !== "capabilities" && a.exists);
174
+ out.push({
175
+ id: "verification",
176
+ service: null,
177
+ path: rel(verificationPath(feature.dir)),
178
+ exists: verification.state !== "absent",
179
+ required: true,
180
+ status: feeders.length === 0 ? "blocked" : verificationStatus(verification),
181
+ blockedBy: feeders.length === 0 ? ["delta", "spec", "arch-spec", "openapi", "asyncapi"] : [],
182
+ });
183
+ return out;
184
+ }
185
+ function fileState(id, file, required, faulted) {
186
+ const { service, path, exists } = file;
187
+ const status = !exists
188
+ ? required
189
+ ? "missing"
190
+ : "done"
191
+ : faulted
192
+ ? "draft"
193
+ : "done";
194
+ return { id, service, path, exists, required, status, blockedBy: [] };
195
+ }
196
+ //# sourceMappingURL=artifacts.js.map