@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,260 @@
1
+ /**
2
+ * Is this document a shape loam can migrate?
3
+ *
4
+ * The living side and the change side are graded separately because they fail
5
+ * differently: a living spec that will not parse is a corpus loam cannot read,
6
+ * while a change spec carries RENAMED pairs, which are the one OpenSpec
7
+ * construct with no loam equivalent and the reason this package has an identity
8
+ * problem to solve at all.
9
+ *
10
+ * Nothing here decides anything — it appends findings to the `Ingest` it was
11
+ * given. The decisions are in `../decide/`.
12
+ */
13
+ import { readFile } from "node:fs/promises";
14
+ import { ownValue } from "../../kernel/records.js";
15
+ import { isRequirementsHeading, parseRequirements, sectionHeadings } from "../../document/parse.js";
16
+ import { requirementIdProblems } from "../../document/spec.js";
17
+ import {} from "./workspace.js";
18
+ import { portable } from "./walk.js";
19
+ import {} from "../model/model.js";
20
+ export function countsFor(requirements) {
21
+ return {
22
+ requirements: requirements.length,
23
+ scenarios: requirements.reduce((sum, requirement) => sum + requirement.scenarios.length, 0),
24
+ };
25
+ }
26
+ export function addCounts(target, source) {
27
+ target.specFiles += source.specFiles;
28
+ target.requirements += source.requirements;
29
+ target.scenarios += source.scenarios;
30
+ }
31
+ export function cleanRenameName(value) {
32
+ return value
33
+ .trim()
34
+ .replace(/^`|`$/g, "")
35
+ .replace(/^###\s+Requirement:\s*/i, "")
36
+ .trim();
37
+ }
38
+ export function changeSpecCoordinates(path, scope) {
39
+ const prefix = scope === "active" ? /^changes\/([^/]+)\/specs\/(.+)\/spec\.md$/ : /^changes\/archive\/([^/]+)\/specs\/(.+)\/spec\.md$/;
40
+ const match = prefix.exec(path);
41
+ return { changeId: match?.[1] ?? "", capability: match?.[2] ?? "" };
42
+ }
43
+ /**
44
+ * Which lines are fenced content, marker included \u2014 the same rule
45
+ * `parseRequirements` and `sectionHeadings` apply, which spec.ts keeps private.
46
+ * Without it a FROM/TO line inside a fenced example of the rename syntax became
47
+ * a phantom pair, and the only cure was editing the OpenSpec source migration
48
+ * promises never to touch.
49
+ */
50
+ export function fencedLines(lines) {
51
+ let fence = null;
52
+ return lines.map((line) => {
53
+ const marker = /^\s*(```|~~~)/.exec(line);
54
+ if (marker === null)
55
+ return fence !== null;
56
+ if (fence === null)
57
+ fence = marker[1];
58
+ else if (fence === marker[1])
59
+ fence = null;
60
+ return true;
61
+ });
62
+ }
63
+ export function renameUsages(path, raw, scope, mapped) {
64
+ const lines = raw.replace(/^\uFEFF/, "").split(/\r?\n/);
65
+ const fenced = fencedLines(lines);
66
+ const headings = sectionHeadings(raw);
67
+ const coordinates = changeSpecCoordinates(path, scope);
68
+ const output = [];
69
+ for (const [headingIndex, heading] of headings.entries()) {
70
+ if (!/^##\s+RENAMED\s+Requirements?\s*:?\s*$/i.test(heading.text))
71
+ continue;
72
+ const end = headings[headingIndex + 1]?.line ?? lines.length + 1;
73
+ const pairs = [];
74
+ let pendingFrom = null;
75
+ for (let line = heading.line; line < end - 1; line += 1) {
76
+ if (fenced[line] === true)
77
+ continue;
78
+ const text = lines[line] ?? "";
79
+ const from = /^\s*-\s*FROM:\s*(.*?)\s*$/i.exec(text);
80
+ if (from !== null) {
81
+ if (pendingFrom !== null)
82
+ pairs.push({ from: pendingFrom, to: null });
83
+ pendingFrom = cleanRenameName(from[1] ?? "");
84
+ continue;
85
+ }
86
+ const to = /^\s*-\s*TO:\s*(.*?)\s*$/i.exec(text);
87
+ if (to !== null) {
88
+ pairs.push({ from: pendingFrom, to: cleanRenameName(to[1] ?? "") });
89
+ pendingFrom = null;
90
+ }
91
+ }
92
+ if (pendingFrom !== null)
93
+ pairs.push({ from: pendingFrom, to: null });
94
+ if (pairs.length === 0)
95
+ pairs.push({ from: null, to: null });
96
+ pairs.forEach((pair, pairIndex) => {
97
+ const key = `${path}:${heading.line}:${pairIndex + 1}`;
98
+ const requirementId = ownValue(mapped, key) ?? null;
99
+ output.push({
100
+ key,
101
+ path,
102
+ line: heading.line,
103
+ scope,
104
+ changeId: coordinates.changeId,
105
+ capability: coordinates.capability,
106
+ from: pair.from,
107
+ to: pair.to,
108
+ existingRequirementId: null,
109
+ requirementId,
110
+ status: requirementId === null ? "needsIdentity" : "mapped",
111
+ });
112
+ });
113
+ }
114
+ return output;
115
+ }
116
+ /**
117
+ * Record one shape loam cannot migrate.
118
+ *
119
+ * The finding is a record rather than three positional strings because code,
120
+ * path and message are all strings and nothing but their order distinguished
121
+ * them — two of the three were transposed at a call site once, and the report
122
+ * named the message as the path.
123
+ */
124
+ export function issue(target, scope, finding) {
125
+ target.push({ ...finding, scope });
126
+ }
127
+ export function inspectLivingShape(path, raw, unsupported) {
128
+ const requirements = parseRequirements(raw);
129
+ if (requirements.length === 0) {
130
+ issue(unsupported, "living", {
131
+ code: "openspec.living-empty",
132
+ path: path,
133
+ message: "Living spec has no parseable Requirement headings.",
134
+ });
135
+ return;
136
+ }
137
+ const outside = requirements.filter((requirement) => requirement.section === undefined || !isRequirementsHeading(requirement.section));
138
+ if (outside.length > 0) {
139
+ issue(unsupported, "living", {
140
+ code: "openspec.living-requirements-outside-section",
141
+ path: path,
142
+ message: `${outside.length} requirement(s) are outside the canonical ## Requirements section.`,
143
+ });
144
+ }
145
+ if (requirements.some((requirement) => requirement.kind !== "BASE")) {
146
+ issue(unsupported, "living", {
147
+ code: "openspec.living-delta-section",
148
+ path: path,
149
+ message: "Living spec contains delta-kind requirements.",
150
+ });
151
+ }
152
+ }
153
+ export function inspectChangeShape(unsupported, path, raw, found) {
154
+ const { scope, renames } = found;
155
+ const requirements = parseRequirements(raw);
156
+ const fileRenames = renames.filter((rename) => rename.path === path);
157
+ for (const rename of fileRenames) {
158
+ if (rename.from === null || rename.from === "" || rename.to === null || rename.to === "") {
159
+ issue(unsupported, scope, {
160
+ code: "openspec.renamed-malformed",
161
+ path: path,
162
+ message: `RENAMED Requirements at line ${rename.line} must contain a FROM/TO pair.`,
163
+ });
164
+ }
165
+ }
166
+ if (requirements.length === 0) {
167
+ // A well-formed rename-only delta is meaningful even though the requirement
168
+ // parser correctly returns no requirement blocks for it.
169
+ if (fileRenames.length === 0) {
170
+ issue(unsupported, scope, {
171
+ code: "openspec.change-empty",
172
+ path: path,
173
+ message: "Change spec has no parseable Requirement headings.",
174
+ });
175
+ }
176
+ return;
177
+ }
178
+ // `## Requirements` is the heading OpenSpec's own living-spec template
179
+ // mandates, so this is the shape a team produces by copying a living spec into
180
+ // a change directory. It parses, it is counted, and nothing routes it: only
181
+ // delta-kind requirements reach the staged feature. Blocking is what keeps the
182
+ // per-change counters and what actually lands the same number.
183
+ const quoted = requirements.filter((requirement) => requirement.kind === "BASE"
184
+ && requirement.section !== undefined
185
+ && isRequirementsHeading(requirement.section));
186
+ if (quoted.length > 0) {
187
+ unsupported.push({
188
+ code: "openspec.change-quoted-requirements",
189
+ path,
190
+ message: `${quoted.length} requirement(s) sit under ## Requirements in a change delta, which stages nothing; re-home them under ADDED, MODIFIED, or REMOVED.`,
191
+ scope,
192
+ });
193
+ }
194
+ const stranded = requirements.filter((requirement) => requirement.kind === "BASE"
195
+ && (requirement.section === undefined || !isRequirementsHeading(requirement.section)));
196
+ if (stranded.length === requirements.length) {
197
+ issue(unsupported, scope, {
198
+ code: "openspec.change-without-delta-sections",
199
+ path: path,
200
+ message: "Change requirements are not under ADDED, MODIFIED, REMOVED, or the explicitly non-merging ## Requirements section.",
201
+ });
202
+ }
203
+ else if (stranded.length > 0) {
204
+ issue(unsupported, scope, {
205
+ code: "openspec.change-requirements-outside-delta-sections",
206
+ path: path,
207
+ message: `${stranded.length} requirement(s) would be stranded outside delta sections.`,
208
+ });
209
+ }
210
+ }
211
+ /**
212
+ * The parsed requirements come back with the counts because both callers wanted
213
+ * them and each opened the file a second time to get its own copy — two reads
214
+ * of the same bytes per spec.md, and two readings of it: the counts came from
215
+ * this parse while the requirement names, and the living corpus every rename is
216
+ * resolved against, came from the other. A file edited mid-audit could be
217
+ * counted from one of them and routed from the other.
218
+ */
219
+ export async function inspectSpecFile(ingest, absolute, kind, scope) {
220
+ const { root, mapping, renamed, unsupported } = ingest;
221
+ const path = portable(root, absolute);
222
+ const raw = await readFile(absolute, "utf8");
223
+ const requirements = parseRequirements(raw);
224
+ const counts = { specFiles: 1, ...countsFor(requirements) };
225
+ const issueScope = kind === "living" ? "living" : scope ?? "active";
226
+ for (const problem of requirementIdProblems(requirements)) {
227
+ if (problem.kind === "invalid") {
228
+ issue(unsupported, issueScope, {
229
+ code: "openspec.requirement-id-invalid",
230
+ path: path,
231
+ message: `Requirement '${problem.requirement}' has invalid Requirement-ID '${problem.value}'; use [A-Za-z][A-Za-z0-9._-]{0,127}.`,
232
+ });
233
+ }
234
+ else if (problem.kind === "repeated") {
235
+ issue(unsupported, issueScope, {
236
+ code: "openspec.requirement-id-repeated",
237
+ path: path,
238
+ message: `Requirement '${problem.requirement}' declares Requirement-ID ${problem.values.length} times; keep exactly one identity line.`,
239
+ });
240
+ }
241
+ else {
242
+ issue(unsupported, issueScope, {
243
+ code: "openspec.requirement-id-duplicate",
244
+ path: path,
245
+ message: `Requirement-ID '${problem.id}' is shared by ${problem.requirements.map((name) => `'${name}'`).join(", ")}.`,
246
+ });
247
+ }
248
+ }
249
+ if (kind === "living") {
250
+ inspectLivingShape(path, raw, unsupported);
251
+ }
252
+ else {
253
+ const changeScope = scope ?? "active";
254
+ const usages = renameUsages(path, raw, changeScope, mapping.renames);
255
+ renamed.push(...usages);
256
+ inspectChangeShape(unsupported, path, raw, { scope: changeScope, renames: usages });
257
+ }
258
+ return { path, counts, requirements };
259
+ }
260
+ //# sourceMappingURL=shape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shape.js","sourceRoot":"","sources":["../../../../src/core/openspec/scan/shape.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Is this document a shape loam can migrate?\n *\n * The living side and the change side are graded separately because they fail\n * differently: a living spec that will not parse is a corpus loam cannot read,\n * while a change spec carries RENAMED pairs, which are the one OpenSpec\n * construct with no loam equivalent and the reason this package has an identity\n * problem to solve at all.\n *\n * Nothing here decides anything — it appends findings to the `Ingest` it was\n * given. The decisions are in `../decide/`.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { ownValue } from \"../../kernel/records.js\";\nimport { isRequirementsHeading, parseRequirements, sectionHeadings } from \"../../document/parse.js\";\nimport { requirementIdProblems } from \"../../document/spec.js\";\nimport { type Ingest } from \"./workspace.js\";\nimport { portable } from \"./walk.js\";\nimport {\n type OpenSpecCounts,\n type OpenSpecRenamedUsage,\n type OpenSpecUnsupportedShape,\n type OpenSpecIssueScope,\n} from \"../model/model.js\";\n\nexport function countsFor(requirements: ReturnType<typeof parseRequirements>): Omit<OpenSpecCounts, \"specFiles\"> {\n return {\n requirements: requirements.length,\n scenarios: requirements.reduce((sum, requirement) => sum + requirement.scenarios.length, 0),\n };\n}\n\nexport function addCounts(target: OpenSpecCounts, source: OpenSpecCounts): void {\n target.specFiles += source.specFiles;\n target.requirements += source.requirements;\n target.scenarios += source.scenarios;\n}\n\nexport function cleanRenameName(value: string): string {\n return value\n .trim()\n .replace(/^`|`$/g, \"\")\n .replace(/^###\\s+Requirement:\\s*/i, \"\")\n .trim();\n}\n\nexport function changeSpecCoordinates(\n path: string,\n scope: \"active\" | \"archive\",\n): { changeId: string; capability: string } {\n const prefix = scope === \"active\" ? /^changes\\/([^/]+)\\/specs\\/(.+)\\/spec\\.md$/ : /^changes\\/archive\\/([^/]+)\\/specs\\/(.+)\\/spec\\.md$/;\n const match = prefix.exec(path);\n return { changeId: match?.[1] ?? \"\", capability: match?.[2] ?? \"\" };\n}\n\n/**\n * Which lines are fenced content, marker included \\u2014 the same rule\n * `parseRequirements` and `sectionHeadings` apply, which spec.ts keeps private.\n * Without it a FROM/TO line inside a fenced example of the rename syntax became\n * a phantom pair, and the only cure was editing the OpenSpec source migration\n * promises never to touch.\n */\nexport function fencedLines(lines: string[]): boolean[] {\n let fence: string | null = null;\n return lines.map((line) => {\n const marker = /^\\s*(```|~~~)/.exec(line);\n if (marker === null) return fence !== null;\n if (fence === null) fence = marker[1]!;\n else if (fence === marker[1]!) fence = null;\n return true;\n });\n}\n\nexport function renameUsages(\n path: string,\n raw: string,\n scope: \"active\" | \"archive\",\n mapped: Record<string, string>,\n): OpenSpecRenamedUsage[] {\n const lines = raw.replace(/^\\uFEFF/, \"\").split(/\\r?\\n/);\n const fenced = fencedLines(lines);\n const headings = sectionHeadings(raw);\n const coordinates = changeSpecCoordinates(path, scope);\n const output: OpenSpecRenamedUsage[] = [];\n for (const [headingIndex, heading] of headings.entries()) {\n if (!/^##\\s+RENAMED\\s+Requirements?\\s*:?\\s*$/i.test(heading.text)) continue;\n const end = headings[headingIndex + 1]?.line ?? lines.length + 1;\n const pairs: Array<{ from: string | null; to: string | null }> = [];\n let pendingFrom: string | null = null;\n for (let line = heading.line; line < end - 1; line += 1) {\n if (fenced[line] === true) continue;\n const text = lines[line] ?? \"\";\n const from = /^\\s*-\\s*FROM:\\s*(.*?)\\s*$/i.exec(text);\n if (from !== null) {\n if (pendingFrom !== null) pairs.push({ from: pendingFrom, to: null });\n pendingFrom = cleanRenameName(from[1] ?? \"\");\n continue;\n }\n const to = /^\\s*-\\s*TO:\\s*(.*?)\\s*$/i.exec(text);\n if (to !== null) {\n pairs.push({ from: pendingFrom, to: cleanRenameName(to[1] ?? \"\") });\n pendingFrom = null;\n }\n }\n if (pendingFrom !== null) pairs.push({ from: pendingFrom, to: null });\n if (pairs.length === 0) pairs.push({ from: null, to: null });\n pairs.forEach((pair, pairIndex) => {\n const key = `${path}:${heading.line}:${pairIndex + 1}`;\n const requirementId = ownValue(mapped, key) ?? null;\n output.push({\n key,\n path,\n line: heading.line,\n scope,\n changeId: coordinates.changeId,\n capability: coordinates.capability,\n from: pair.from,\n to: pair.to,\n existingRequirementId: null,\n requirementId,\n status: requirementId === null ? \"needsIdentity\" : \"mapped\",\n });\n });\n }\n return output;\n}\n\n/**\n * Record one shape loam cannot migrate.\n *\n * The finding is a record rather than three positional strings because code,\n * path and message are all strings and nothing but their order distinguished\n * them — two of the three were transposed at a call site once, and the report\n * named the message as the path.\n */\nexport function issue(\n target: OpenSpecUnsupportedShape[],\n scope: OpenSpecIssueScope,\n finding: { code: string; path: string; message: string },\n): void {\n target.push({ ...finding, scope });\n}\n\nexport function inspectLivingShape(\n path: string,\n raw: string,\n unsupported: OpenSpecUnsupportedShape[],\n): void {\n const requirements = parseRequirements(raw);\n if (requirements.length === 0) {\n issue(unsupported, \"living\", {\n code: \"openspec.living-empty\",\n path: path,\n message: \"Living spec has no parseable Requirement headings.\",\n });\n return;\n }\n const outside = requirements.filter(\n (requirement) => requirement.section === undefined || !isRequirementsHeading(requirement.section),\n );\n if (outside.length > 0) {\n issue(unsupported, \"living\", {\n code: \"openspec.living-requirements-outside-section\",\n path: path,\n message: `${outside.length} requirement(s) are outside the canonical ## Requirements section.`,\n });\n }\n if (requirements.some((requirement) => requirement.kind !== \"BASE\")) {\n issue(unsupported, \"living\", {\n code: \"openspec.living-delta-section\",\n path: path,\n message: \"Living spec contains delta-kind requirements.\",\n });\n }\n}\n\nexport function inspectChangeShape(\n unsupported: OpenSpecUnsupportedShape[],\n path: string,\n raw: string,\n found: { scope: \"active\" | \"archive\"; renames: OpenSpecRenamedUsage[] },\n): void {\n const { scope, renames } = found;\n const requirements = parseRequirements(raw);\n const fileRenames = renames.filter((rename) => rename.path === path);\n for (const rename of fileRenames) {\n if (rename.from === null || rename.from === \"\" || rename.to === null || rename.to === \"\") {\n issue(unsupported, scope, {\n code: \"openspec.renamed-malformed\",\n path: path,\n message: `RENAMED Requirements at line ${rename.line} must contain a FROM/TO pair.`,\n });\n }\n }\n if (requirements.length === 0) {\n // A well-formed rename-only delta is meaningful even though the requirement\n // parser correctly returns no requirement blocks for it.\n if (fileRenames.length === 0) {\n issue(unsupported, scope, {\n code: \"openspec.change-empty\",\n path: path,\n message: \"Change spec has no parseable Requirement headings.\",\n });\n }\n return;\n }\n // `## Requirements` is the heading OpenSpec's own living-spec template\n // mandates, so this is the shape a team produces by copying a living spec into\n // a change directory. It parses, it is counted, and nothing routes it: only\n // delta-kind requirements reach the staged feature. Blocking is what keeps the\n // per-change counters and what actually lands the same number.\n const quoted = requirements.filter(\n (requirement) => requirement.kind === \"BASE\"\n && requirement.section !== undefined\n && isRequirementsHeading(requirement.section),\n );\n if (quoted.length > 0) {\n unsupported.push({\n code: \"openspec.change-quoted-requirements\",\n path,\n message: `${quoted.length} requirement(s) sit under ## Requirements in a change delta, which stages nothing; re-home them under ADDED, MODIFIED, or REMOVED.`,\n scope,\n });\n }\n const stranded = requirements.filter(\n (requirement) => requirement.kind === \"BASE\"\n && (requirement.section === undefined || !isRequirementsHeading(requirement.section)),\n );\n if (stranded.length === requirements.length) {\n issue(unsupported, scope, {\n code: \"openspec.change-without-delta-sections\",\n path: path,\n message: \"Change requirements are not under ADDED, MODIFIED, REMOVED, or the explicitly non-merging ## Requirements section.\",\n });\n } else if (stranded.length > 0) {\n issue(unsupported, scope, {\n code: \"openspec.change-requirements-outside-delta-sections\",\n path: path,\n message: `${stranded.length} requirement(s) would be stranded outside delta sections.`,\n });\n }\n}\n\n/**\n * The parsed requirements come back with the counts because both callers wanted\n * them and each opened the file a second time to get its own copy — two reads\n * of the same bytes per spec.md, and two readings of it: the counts came from\n * this parse while the requirement names, and the living corpus every rename is\n * resolved against, came from the other. A file edited mid-audit could be\n * counted from one of them and routed from the other.\n */\nexport async function inspectSpecFile(\n ingest: Ingest,\n absolute: string,\n kind: \"living\" | \"change\",\n scope: \"active\" | \"archive\" | null,\n): Promise<{ path: string; counts: OpenSpecCounts; requirements: ReturnType<typeof parseRequirements> }> {\n const { root, mapping, renamed, unsupported } = ingest;\n const path = portable(root, absolute);\n const raw = await readFile(absolute, \"utf8\");\n const requirements = parseRequirements(raw);\n const counts = { specFiles: 1, ...countsFor(requirements) };\n const issueScope = kind === \"living\" ? \"living\" : scope ?? \"active\";\n for (const problem of requirementIdProblems(requirements)) {\n if (problem.kind === \"invalid\") {\n issue(unsupported, issueScope, {\n code: \"openspec.requirement-id-invalid\",\n path: path,\n message: `Requirement '${problem.requirement}' has invalid Requirement-ID '${problem.value}'; use [A-Za-z][A-Za-z0-9._-]{0,127}.`,\n });\n } else if (problem.kind === \"repeated\") {\n issue(unsupported, issueScope, {\n code: \"openspec.requirement-id-repeated\",\n path: path,\n message: `Requirement '${problem.requirement}' declares Requirement-ID ${problem.values.length} times; keep exactly one identity line.`,\n });\n } else {\n issue(unsupported, issueScope, {\n code: \"openspec.requirement-id-duplicate\",\n path: path,\n message: `Requirement-ID '${problem.id}' is shared by ${problem.requirements.map((name) => `'${name}'`).join(\", \")}.`,\n });\n }\n }\n if (kind === \"living\") {\n inspectLivingShape(path, raw, unsupported);\n } else {\n const changeScope = scope ?? \"active\";\n const usages = renameUsages(path, raw, changeScope, mapping.renames);\n renamed.push(...usages);\n inspectChangeShape(unsupported, path, raw, { scope: changeScope, renames: usages });\n }\n return { path, counts, requirements };\n}\n"]}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Reading the tree: which directories exist, which files are under them, which
3
+ * of them are symlinks, and the digest that binds a mapping to what was audited.
4
+ *
5
+ * Symlinks are enumerated rather than followed. The whole package reads
6
+ * somebody else's repository, so "what is under this directory" has to be a
7
+ * question about THIS workspace — a link out of it is a finding, never a file
8
+ * to walk into.
9
+ */
10
+ import { createHash } from "node:crypto";
11
+ import { lstat, readFile, readdir, stat } from "node:fs/promises";
12
+ import { dirname, isAbsolute, join, relative, sep } from "node:path";
13
+ import { parse as parseYaml } from "yaml";
14
+ import { asRecord } from "../../kernel/records.js";
15
+ import { compareIds } from "../../repo/entries.js";
16
+ export async function isDirectory(path) {
17
+ try {
18
+ return (await stat(path)).isDirectory();
19
+ }
20
+ catch {
21
+ return false;
22
+ }
23
+ }
24
+ export function portable(root, path) {
25
+ return relative(root, path).split(sep).join("/");
26
+ }
27
+ /** Stable inventory namespace: planning-root relative, with one explicit external prefix. */
28
+ export function sourceInventoryPath(root, absolute) {
29
+ const rel = relative(root, absolute);
30
+ if (rel === "" || (!rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel))) {
31
+ return rel.split(sep).join("/");
32
+ }
33
+ return `@workspace/${portable(dirname(root), absolute)}`;
34
+ }
35
+ export async function subdirs(path) {
36
+ if (!await isDirectory(path))
37
+ return [];
38
+ return (await readdir(path, { withFileTypes: true }))
39
+ .filter((entry) => entry.isDirectory() && !entry.name.startsWith("."))
40
+ .map((entry) => entry.name)
41
+ .sort(compareIds);
42
+ }
43
+ /**
44
+ * Exactly what `subdirs` drops. walkFiles does not drop it, so a change behind a
45
+ * dot-directory was never enumerated as a change while its files were still
46
+ * classified, counted and given a disposition slot — a decision recorded as
47
+ * selected for an artifact nothing migrates.
48
+ */
49
+ export async function hiddenSubdirs(path) {
50
+ if (!await isDirectory(path))
51
+ return [];
52
+ return (await readdir(path, { withFileTypes: true }))
53
+ .filter((entry) => entry.isDirectory() && entry.name.startsWith("."))
54
+ .map((entry) => entry.name)
55
+ .sort(compareIds);
56
+ }
57
+ export async function walkFiles(path) {
58
+ if (!await isDirectory(path))
59
+ return [];
60
+ const files = [];
61
+ const walk = async (dir) => {
62
+ const entries = (await readdir(dir, { withFileTypes: true }))
63
+ .filter((entry) => entry.name !== ".git")
64
+ .sort((a, b) => compareIds(a.name, b.name));
65
+ for (const entry of entries) {
66
+ const absolute = join(dir, entry.name);
67
+ if (entry.isDirectory())
68
+ await walk(absolute);
69
+ else if (entry.isFile())
70
+ files.push(absolute);
71
+ }
72
+ };
73
+ await walk(path);
74
+ return files;
75
+ }
76
+ /** Inventory symbolic links without following them into unbound source trees. */
77
+ export async function walkSymlinks(path) {
78
+ if (!await isDirectory(path))
79
+ return [];
80
+ const links = [];
81
+ const walk = async (dir) => {
82
+ const entries = (await readdir(dir, { withFileTypes: true }))
83
+ .filter((entry) => entry.name !== ".git")
84
+ .sort((a, b) => compareIds(a.name, b.name));
85
+ for (const entry of entries) {
86
+ const absolute = join(dir, entry.name);
87
+ if (entry.isSymbolicLink())
88
+ links.push(absolute);
89
+ else if (entry.isDirectory())
90
+ await walk(absolute);
91
+ }
92
+ };
93
+ await walk(path);
94
+ return links;
95
+ }
96
+ export async function isSymbolicLink(path) {
97
+ try {
98
+ return (await lstat(path)).isSymbolicLink();
99
+ }
100
+ catch {
101
+ return false;
102
+ }
103
+ }
104
+ /**
105
+ * `Array.isArray` narrows an `unknown` to `any[]`, which is how a checked
106
+ * element type went on being asserted back in a line later. Deciding both
107
+ * halves in one predicate makes the narrowing carry the check.
108
+ */
109
+ export function isStringArray(value) {
110
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
111
+ }
112
+ export async function yamlRecord(path) {
113
+ const parsed = parseYaml(await readFile(path, "utf8"));
114
+ const record = asRecord(parsed);
115
+ if (record === null)
116
+ throw new Error("expected a YAML mapping");
117
+ return record;
118
+ }
119
+ export async function digestInventory(root, files) {
120
+ const hash = createHash("sha256");
121
+ // v3 narrowed the covered set to living + active. The version string is what
122
+ // stops a v2 mapping from being accepted against a v3 reading of the same tree.
123
+ hash.update("loam-openspec-inventory-v3\0");
124
+ for (const absolute of [...new Set(files)].sort((a, b) => compareIds(sourceInventoryPath(root, a), sourceInventoryPath(root, b)))) {
125
+ const path = sourceInventoryPath(root, absolute);
126
+ const bytes = await readFile(absolute);
127
+ hash.update(`${path.length}:${path}:${bytes.length}:`);
128
+ hash.update(bytes);
129
+ hash.update("\0");
130
+ }
131
+ return `sha256:${hash.digest("hex")}`;
132
+ }
133
+ //# sourceMappingURL=walk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walk.js","sourceRoot":"","sources":["../../../../src/core/openspec/scan/walk.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Reading the tree: which directories exist, which files are under them, which\n * of them are symlinks, and the digest that binds a mapping to what was audited.\n *\n * Symlinks are enumerated rather than followed. The whole package reads\n * somebody else's repository, so \"what is under this directory\" has to be a\n * question about THIS workspace — a link out of it is a finding, never a file\n * to walk into.\n */\nimport { createHash } from \"node:crypto\";\nimport { lstat, readFile, readdir, stat } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, relative, sep } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\nimport { asRecord } from \"../../kernel/records.js\";\nimport { compareIds } from \"../../repo/entries.js\";\n\nexport async function isDirectory(path: string): Promise<boolean> {\n try {\n return (await stat(path)).isDirectory();\n } catch {\n return false;\n }\n}\n\nexport function portable(root: string, path: string): string {\n return relative(root, path).split(sep).join(\"/\");\n}\n\n/** Stable inventory namespace: planning-root relative, with one explicit external prefix. */\nexport function sourceInventoryPath(root: string, absolute: string): string {\n const rel = relative(root, absolute);\n if (rel === \"\" || (!rel.startsWith(`..${sep}`) && rel !== \"..\" && !isAbsolute(rel))) {\n return rel.split(sep).join(\"/\");\n }\n return `@workspace/${portable(dirname(root), absolute)}`;\n}\n\nexport async function subdirs(path: string): Promise<string[]> {\n if (!await isDirectory(path)) return [];\n return (await readdir(path, { withFileTypes: true }))\n .filter((entry) => entry.isDirectory() && !entry.name.startsWith(\".\"))\n .map((entry) => entry.name)\n .sort(compareIds);\n}\n\n/**\n * Exactly what `subdirs` drops. walkFiles does not drop it, so a change behind a\n * dot-directory was never enumerated as a change while its files were still\n * classified, counted and given a disposition slot — a decision recorded as\n * selected for an artifact nothing migrates.\n */\nexport async function hiddenSubdirs(path: string): Promise<string[]> {\n if (!await isDirectory(path)) return [];\n return (await readdir(path, { withFileTypes: true }))\n .filter((entry) => entry.isDirectory() && entry.name.startsWith(\".\"))\n .map((entry) => entry.name)\n .sort(compareIds);\n}\n\nexport async function walkFiles(path: string): Promise<string[]> {\n if (!await isDirectory(path)) return [];\n const files: string[] = [];\n const walk = async (dir: string): Promise<void> => {\n const entries = (await readdir(dir, { withFileTypes: true }))\n .filter((entry) => entry.name !== \".git\")\n .sort((a, b) => compareIds(a.name, b.name));\n for (const entry of entries) {\n const absolute = join(dir, entry.name);\n if (entry.isDirectory()) await walk(absolute);\n else if (entry.isFile()) files.push(absolute);\n }\n };\n await walk(path);\n return files;\n}\n\n/** Inventory symbolic links without following them into unbound source trees. */\nexport async function walkSymlinks(path: string): Promise<string[]> {\n if (!await isDirectory(path)) return [];\n const links: string[] = [];\n const walk = async (dir: string): Promise<void> => {\n const entries = (await readdir(dir, { withFileTypes: true }))\n .filter((entry) => entry.name !== \".git\")\n .sort((a, b) => compareIds(a.name, b.name));\n for (const entry of entries) {\n const absolute = join(dir, entry.name);\n if (entry.isSymbolicLink()) links.push(absolute);\n else if (entry.isDirectory()) await walk(absolute);\n }\n };\n await walk(path);\n return links;\n}\n\nexport async function isSymbolicLink(path: string): Promise<boolean> {\n try {\n return (await lstat(path)).isSymbolicLink();\n } catch {\n return false;\n }\n}\n\n/**\n * `Array.isArray` narrows an `unknown` to `any[]`, which is how a checked\n * element type went on being asserted back in a line later. Deciding both\n * halves in one predicate makes the narrowing carry the check.\n */\nexport function isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((item) => typeof item === \"string\");\n}\n\nexport async function yamlRecord(path: string): Promise<Record<string, unknown>> {\n const parsed: unknown = parseYaml(await readFile(path, \"utf8\"));\n const record = asRecord(parsed);\n if (record === null) throw new Error(\"expected a YAML mapping\");\n return record;\n}\n\nexport async function digestInventory(root: string, files: string[]): Promise<string> {\n const hash = createHash(\"sha256\");\n // v3 narrowed the covered set to living + active. The version string is what\n // stops a v2 mapping from being accepted against a v3 reading of the same tree.\n hash.update(\"loam-openspec-inventory-v3\\0\");\n for (const absolute of [...new Set(files)].sort((a, b) => compareIds(sourceInventoryPath(root, a), sourceInventoryPath(root, b)))) {\n const path = sourceInventoryPath(root, absolute);\n const bytes = await readFile(absolute);\n hash.update(`${path.length}:${path}:${bytes.length}:`);\n hash.update(bytes);\n hash.update(\"\\0\");\n }\n return `sha256:${hash.digest(\"hex\")}`;\n}\n"]}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Where the workspace is, how its paths are spelled, and the record every
3
+ * reader of it carries.
4
+ *
5
+ * `Ingest` exists because the five readers below it all need the same four
6
+ * things — the root, the mapping being checked, and the two lists they append
7
+ * findings to — and none of them may take four parameters plus its own. It is
8
+ * the read in progress, so the accumulators live in it: a scan that returned
9
+ * its findings instead would have to merge five arrays whose ORDER is what the
10
+ * `--json` contract pins.
11
+ */
12
+ import { existsSync } from "node:fs";
13
+ import { realpath } from "node:fs/promises";
14
+ import { basename, dirname, join, resolve } from "node:path";
15
+ import {} from "../model/mapping.js";
16
+ import { OpenSpecRootError } from "../model/model.js";
17
+ import { isDirectory, isSymbolicLink } from "./walk.js";
18
+ export async function looksLikeOpenSpec(path) {
19
+ return existsSync(join(path, "config.yaml"))
20
+ || existsSync(join(path, "project.md"))
21
+ || existsSync(join(path, ".openspec.yaml"))
22
+ || await isDirectory(join(path, "specs"))
23
+ || await isDirectory(join(path, "changes"));
24
+ }
25
+ export async function locateOpenSpecRoot(input) {
26
+ const requestedRoot = resolve(input);
27
+ if (!await isDirectory(requestedRoot)) {
28
+ throw new OpenSpecRootError(`OpenSpec root is missing or is not a directory: ${requestedRoot}`);
29
+ }
30
+ const inputRoot = await realpath(requestedRoot);
31
+ const nested = join(inputRoot, "openspec");
32
+ const storeMarker = join(inputRoot, ".openspec-store", "store.yaml");
33
+ const hasNestedShape = await looksLikeOpenSpec(nested);
34
+ const hasDirectShape = await looksLikeOpenSpec(inputRoot);
35
+ if (hasNestedShape && hasDirectShape) {
36
+ throw new OpenSpecRootError(`Ambiguous OpenSpec root ${inputRoot}: both the directory itself and ${nested} contain OpenSpec planning shapes. Pass the intended root explicitly.`);
37
+ }
38
+ if (hasNestedShape) {
39
+ await assertPlanningRootNotLinked(nested);
40
+ return { inputRoot, root: nested, kind: existsSync(storeMarker) ? "store" : "project" };
41
+ }
42
+ // The shape decides the root, the Store marker only decides the kind. Deciding
43
+ // "store" first sent `root` to <store>/openspec for a checkout that keeps its
44
+ // planning shape at the checkout root: a directory that does not exist reads
45
+ // as an empty, fully compatible workspace, and apply then stages nothing.
46
+ if (hasDirectShape) {
47
+ const parentStoreMarker = basename(inputRoot) === "openspec"
48
+ ? join(dirname(inputRoot), ".openspec-store", "store.yaml")
49
+ : "";
50
+ const store = existsSync(storeMarker) || (parentStoreMarker !== "" && existsSync(parentStoreMarker));
51
+ return { inputRoot, root: inputRoot, kind: store ? "store" : "openspec-root" };
52
+ }
53
+ // OpenSpec stores may omit empty planning directories. Keep their canonical
54
+ // future root at <store>/openspec so paths do not change after the first spec
55
+ // — but only when that directory is really there. Auditing a root that does
56
+ // not exist is how a Store checkout came back `ready` over invisible content.
57
+ if (existsSync(storeMarker)) {
58
+ await assertPlanningRootNotLinked(nested);
59
+ if (!await isDirectory(nested)) {
60
+ throw new OpenSpecRootError(`Store checkout ${inputRoot} has no OpenSpec planning content: neither ${nested} nor config.yaml, project.md, specs/ or changes/ at the checkout root.`);
61
+ }
62
+ return { inputRoot, root: nested, kind: "store" };
63
+ }
64
+ throw new OpenSpecRootError(`No OpenSpec workspace found at ${inputRoot} (expected openspec/config.yaml, specs/, changes/, project.md, or .openspec-store/store.yaml).`);
65
+ }
66
+ export async function assertPlanningRootNotLinked(path) {
67
+ if (await isSymbolicLink(path)) {
68
+ throw new OpenSpecRootError(`OpenSpec planning root must not be a symbolic link: ${path}. Pass the canonical planning directory explicitly.`);
69
+ }
70
+ }
71
+ //# sourceMappingURL=workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../../../src/core/openspec/scan/workspace.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Where the workspace is, how its paths are spelled, and the record every\n * reader of it carries.\n *\n * `Ingest` exists because the five readers below it all need the same four\n * things — the root, the mapping being checked, and the two lists they append\n * findings to — and none of them may take four parameters plus its own. It is\n * the read in progress, so the accumulators live in it: a scan that returned\n * its findings instead would have to merge five arrays whose ORDER is what the\n * `--json` contract pins.\n */\nimport { existsSync } from \"node:fs\";\nimport { realpath } from \"node:fs/promises\";\nimport { basename, dirname, join, resolve } from \"node:path\";\nimport { type OpenSpecMapping } from \"../model/mapping.js\";\nimport { type OpenSpecRenamedUsage, type OpenSpecUnsupportedShape, OpenSpecRootError } from \"../model/model.js\";\nimport { isDirectory, isSymbolicLink } from \"./walk.js\";\n\n/** The one read of one OpenSpec workspace, in progress. */\nexport interface Ingest {\n /** The canonical `openspec/` directory every path below is spelled relative to. */\n root: string;\n /** The mapping being graded against this workspace; empty when auditing. */\n mapping: OpenSpecMapping;\n /** Every RENAMED pair seen, resolved later against the living source. */\n renamed: OpenSpecRenamedUsage[];\n /** Shapes that block migration. Archive-scope findings go to their own list. */\n unsupported: OpenSpecUnsupportedShape[];\n}\n\nexport async function looksLikeOpenSpec(path: string): Promise<boolean> {\n return existsSync(join(path, \"config.yaml\"))\n || existsSync(join(path, \"project.md\"))\n || existsSync(join(path, \".openspec.yaml\"))\n || await isDirectory(join(path, \"specs\"))\n || await isDirectory(join(path, \"changes\"));\n}\n\nexport async function locateOpenSpecRoot(\n input: string,\n): Promise<{ inputRoot: string; root: string; kind: \"project\" | \"store\" | \"openspec-root\" }> {\n const requestedRoot = resolve(input);\n if (!await isDirectory(requestedRoot)) {\n throw new OpenSpecRootError(`OpenSpec root is missing or is not a directory: ${requestedRoot}`);\n }\n const inputRoot = await realpath(requestedRoot);\n const nested = join(inputRoot, \"openspec\");\n const storeMarker = join(inputRoot, \".openspec-store\", \"store.yaml\");\n const hasNestedShape = await looksLikeOpenSpec(nested);\n const hasDirectShape = await looksLikeOpenSpec(inputRoot);\n if (hasNestedShape && hasDirectShape) {\n throw new OpenSpecRootError(\n `Ambiguous OpenSpec root ${inputRoot}: both the directory itself and ${nested} contain OpenSpec planning shapes. Pass the intended root explicitly.`,\n );\n }\n if (hasNestedShape) {\n await assertPlanningRootNotLinked(nested);\n return { inputRoot, root: nested, kind: existsSync(storeMarker) ? \"store\" : \"project\" };\n }\n // The shape decides the root, the Store marker only decides the kind. Deciding\n // \"store\" first sent `root` to <store>/openspec for a checkout that keeps its\n // planning shape at the checkout root: a directory that does not exist reads\n // as an empty, fully compatible workspace, and apply then stages nothing.\n if (hasDirectShape) {\n const parentStoreMarker = basename(inputRoot) === \"openspec\"\n ? join(dirname(inputRoot), \".openspec-store\", \"store.yaml\")\n : \"\";\n const store = existsSync(storeMarker) || (parentStoreMarker !== \"\" && existsSync(parentStoreMarker));\n return { inputRoot, root: inputRoot, kind: store ? \"store\" : \"openspec-root\" };\n }\n // OpenSpec stores may omit empty planning directories. Keep their canonical\n // future root at <store>/openspec so paths do not change after the first spec\n // — but only when that directory is really there. Auditing a root that does\n // not exist is how a Store checkout came back `ready` over invisible content.\n if (existsSync(storeMarker)) {\n await assertPlanningRootNotLinked(nested);\n if (!await isDirectory(nested)) {\n throw new OpenSpecRootError(\n `Store checkout ${inputRoot} has no OpenSpec planning content: neither ${nested} nor config.yaml, project.md, specs/ or changes/ at the checkout root.`,\n );\n }\n return { inputRoot, root: nested, kind: \"store\" };\n }\n throw new OpenSpecRootError(\n `No OpenSpec workspace found at ${inputRoot} (expected openspec/config.yaml, specs/, changes/, project.md, or .openspec-store/store.yaml).`,\n );\n}\n\nexport async function assertPlanningRootNotLinked(path: string): Promise<void> {\n if (await isSymbolicLink(path)) {\n throw new OpenSpecRootError(\n `OpenSpec planning root must not be a symbolic link: ${path}. Pass the canonical planning directory explicitly.`,\n );\n }\n}\n"]}