@descryy/core 0.1.0

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 (366) hide show
  1. package/LICENSE +21 -0
  2. package/dist/capabilities/index.d.ts +3 -0
  3. package/dist/capabilities/index.d.ts.map +1 -0
  4. package/dist/capabilities/index.js +2 -0
  5. package/dist/capabilities/index.js.map +1 -0
  6. package/dist/capabilities/preconditions.d.ts +99 -0
  7. package/dist/capabilities/preconditions.d.ts.map +1 -0
  8. package/dist/capabilities/preconditions.js +94 -0
  9. package/dist/capabilities/preconditions.js.map +1 -0
  10. package/dist/contracts/engine.d.ts +90 -0
  11. package/dist/contracts/engine.d.ts.map +1 -0
  12. package/dist/contracts/engine.js +291 -0
  13. package/dist/contracts/engine.js.map +1 -0
  14. package/dist/contracts/index.d.ts +9 -0
  15. package/dist/contracts/index.d.ts.map +1 -0
  16. package/dist/contracts/index.js +5 -0
  17. package/dist/contracts/index.js.map +1 -0
  18. package/dist/contracts/orphans.d.ts +96 -0
  19. package/dist/contracts/orphans.d.ts.map +1 -0
  20. package/dist/contracts/orphans.js +241 -0
  21. package/dist/contracts/orphans.js.map +1 -0
  22. package/dist/contracts/paths.d.ts +132 -0
  23. package/dist/contracts/paths.d.ts.map +1 -0
  24. package/dist/contracts/paths.js +245 -0
  25. package/dist/contracts/paths.js.map +1 -0
  26. package/dist/contracts/shapes.d.ts +99 -0
  27. package/dist/contracts/shapes.d.ts.map +1 -0
  28. package/dist/contracts/shapes.js +196 -0
  29. package/dist/contracts/shapes.js.map +1 -0
  30. package/dist/governance/budget.d.ts +89 -0
  31. package/dist/governance/budget.d.ts.map +1 -0
  32. package/dist/governance/budget.js +101 -0
  33. package/dist/governance/budget.js.map +1 -0
  34. package/dist/governance/candidate-boundary.d.ts +49 -0
  35. package/dist/governance/candidate-boundary.d.ts.map +1 -0
  36. package/dist/governance/candidate-boundary.js +51 -0
  37. package/dist/governance/candidate-boundary.js.map +1 -0
  38. package/dist/governance/escalation.d.ts +63 -0
  39. package/dist/governance/escalation.d.ts.map +1 -0
  40. package/dist/governance/escalation.js +72 -0
  41. package/dist/governance/escalation.js.map +1 -0
  42. package/dist/governance/fact-boundary.d.ts +77 -0
  43. package/dist/governance/fact-boundary.d.ts.map +1 -0
  44. package/dist/governance/fact-boundary.js +91 -0
  45. package/dist/governance/fact-boundary.js.map +1 -0
  46. package/dist/governance/finding-funnel.d.ts +109 -0
  47. package/dist/governance/finding-funnel.d.ts.map +1 -0
  48. package/dist/governance/finding-funnel.js +137 -0
  49. package/dist/governance/finding-funnel.js.map +1 -0
  50. package/dist/governance/index.d.ts +11 -0
  51. package/dist/governance/index.d.ts.map +1 -0
  52. package/dist/governance/index.js +6 -0
  53. package/dist/governance/index.js.map +1 -0
  54. package/dist/graph/build.d.ts +127 -0
  55. package/dist/graph/build.d.ts.map +1 -0
  56. package/dist/graph/build.js +0 -0
  57. package/dist/graph/build.js.map +1 -0
  58. package/dist/graph/cross-language.d.ts +70 -0
  59. package/dist/graph/cross-language.d.ts.map +1 -0
  60. package/dist/graph/cross-language.js +104 -0
  61. package/dist/graph/cross-language.js.map +1 -0
  62. package/dist/graph/index.d.ts +11 -0
  63. package/dist/graph/index.d.ts.map +1 -0
  64. package/dist/graph/index.js +6 -0
  65. package/dist/graph/index.js.map +1 -0
  66. package/dist/graph/merge.d.ts +95 -0
  67. package/dist/graph/merge.d.ts.map +1 -0
  68. package/dist/graph/merge.js +131 -0
  69. package/dist/graph/merge.js.map +1 -0
  70. package/dist/graph/persist.d.ts +42 -0
  71. package/dist/graph/persist.d.ts.map +1 -0
  72. package/dist/graph/persist.js +52 -0
  73. package/dist/graph/persist.js.map +1 -0
  74. package/dist/graph/runtime-confirmation.d.ts +169 -0
  75. package/dist/graph/runtime-confirmation.d.ts.map +1 -0
  76. package/dist/graph/runtime-confirmation.js +318 -0
  77. package/dist/graph/runtime-confirmation.js.map +1 -0
  78. package/dist/index.d.ts +18 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +19 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/ledger/index.d.ts +5 -0
  83. package/dist/ledger/index.d.ts.map +1 -0
  84. package/dist/ledger/index.js +3 -0
  85. package/dist/ledger/index.js.map +1 -0
  86. package/dist/ledger/mint.d.ts +81 -0
  87. package/dist/ledger/mint.d.ts.map +1 -0
  88. package/dist/ledger/mint.js +138 -0
  89. package/dist/ledger/mint.js.map +1 -0
  90. package/dist/ledger/resolve.d.ts +124 -0
  91. package/dist/ledger/resolve.d.ts.map +1 -0
  92. package/dist/ledger/resolve.js +290 -0
  93. package/dist/ledger/resolve.js.map +1 -0
  94. package/dist/multipr/fingerprint.d.ts +56 -0
  95. package/dist/multipr/fingerprint.d.ts.map +1 -0
  96. package/dist/multipr/fingerprint.js +169 -0
  97. package/dist/multipr/fingerprint.js.map +1 -0
  98. package/dist/multipr/hidden-dependency.d.ts +120 -0
  99. package/dist/multipr/hidden-dependency.d.ts.map +1 -0
  100. package/dist/multipr/hidden-dependency.js +185 -0
  101. package/dist/multipr/hidden-dependency.js.map +1 -0
  102. package/dist/multipr/index.d.ts +14 -0
  103. package/dist/multipr/index.d.ts.map +1 -0
  104. package/dist/multipr/index.js +8 -0
  105. package/dist/multipr/index.js.map +1 -0
  106. package/dist/multipr/mechanical.d.ts +239 -0
  107. package/dist/multipr/mechanical.d.ts.map +1 -0
  108. package/dist/multipr/mechanical.js +333 -0
  109. package/dist/multipr/mechanical.js.map +1 -0
  110. package/dist/multipr/migration-heads.d.ts +160 -0
  111. package/dist/multipr/migration-heads.d.ts.map +1 -0
  112. package/dist/multipr/migration-heads.js +212 -0
  113. package/dist/multipr/migration-heads.js.map +1 -0
  114. package/dist/multipr/overlap.d.ts +186 -0
  115. package/dist/multipr/overlap.d.ts.map +1 -0
  116. package/dist/multipr/overlap.js +254 -0
  117. package/dist/multipr/overlap.js.map +1 -0
  118. package/dist/multipr/scope-store.d.ts +105 -0
  119. package/dist/multipr/scope-store.d.ts.map +1 -0
  120. package/dist/multipr/scope-store.js +182 -0
  121. package/dist/multipr/scope-store.js.map +1 -0
  122. package/dist/multipr/superseded.d.ts +133 -0
  123. package/dist/multipr/superseded.d.ts.map +1 -0
  124. package/dist/multipr/superseded.js +128 -0
  125. package/dist/multipr/superseded.js.map +1 -0
  126. package/dist/query/confirmed-facts.d.ts +56 -0
  127. package/dist/query/confirmed-facts.d.ts.map +1 -0
  128. package/dist/query/confirmed-facts.js +64 -0
  129. package/dist/query/confirmed-facts.js.map +1 -0
  130. package/dist/query/declared-value-closure.d.ts +126 -0
  131. package/dist/query/declared-value-closure.d.ts.map +1 -0
  132. package/dist/query/declared-value-closure.js +156 -0
  133. package/dist/query/declared-value-closure.js.map +1 -0
  134. package/dist/query/index.d.ts +28 -0
  135. package/dist/query/index.d.ts.map +1 -0
  136. package/dist/query/index.js +15 -0
  137. package/dist/query/index.js.map +1 -0
  138. package/dist/query/memory.d.ts +20 -0
  139. package/dist/query/memory.d.ts.map +1 -0
  140. package/dist/query/memory.js +55 -0
  141. package/dist/query/memory.js.map +1 -0
  142. package/dist/query/provider.d.ts +127 -0
  143. package/dist/query/provider.d.ts.map +1 -0
  144. package/dist/query/provider.js +54 -0
  145. package/dist/query/provider.js.map +1 -0
  146. package/dist/query/queries.d.ts +119 -0
  147. package/dist/query/queries.d.ts.map +1 -0
  148. package/dist/query/queries.js +267 -0
  149. package/dist/query/queries.js.map +1 -0
  150. package/dist/query/refusal-fetch.d.ts +114 -0
  151. package/dist/query/refusal-fetch.d.ts.map +1 -0
  152. package/dist/query/refusal-fetch.js +200 -0
  153. package/dist/query/refusal-fetch.js.map +1 -0
  154. package/dist/query/refusal-questions.d.ts +123 -0
  155. package/dist/query/refusal-questions.d.ts.map +1 -0
  156. package/dist/query/refusal-questions.js +122 -0
  157. package/dist/query/refusal-questions.js.map +1 -0
  158. package/dist/query/root-cause-score.d.ts +218 -0
  159. package/dist/query/root-cause-score.d.ts.map +1 -0
  160. package/dist/query/root-cause-score.js +251 -0
  161. package/dist/query/root-cause-score.js.map +1 -0
  162. package/dist/query/row-closure-picture.d.ts +108 -0
  163. package/dist/query/row-closure-picture.d.ts.map +1 -0
  164. package/dist/query/row-closure-picture.js +131 -0
  165. package/dist/query/row-closure-picture.js.map +1 -0
  166. package/dist/query/similar-incidents.d.ts +76 -0
  167. package/dist/query/similar-incidents.d.ts.map +1 -0
  168. package/dist/query/similar-incidents.js +169 -0
  169. package/dist/query/similar-incidents.js.map +1 -0
  170. package/dist/query/sqlite.d.ts +13 -0
  171. package/dist/query/sqlite.d.ts.map +1 -0
  172. package/dist/query/sqlite.js +54 -0
  173. package/dist/query/sqlite.js.map +1 -0
  174. package/dist/query/traverse.d.ts +110 -0
  175. package/dist/query/traverse.d.ts.map +1 -0
  176. package/dist/query/traverse.js +0 -0
  177. package/dist/query/traverse.js.map +1 -0
  178. package/dist/query/unresolved.d.ts +195 -0
  179. package/dist/query/unresolved.d.ts.map +1 -0
  180. package/dist/query/unresolved.js +225 -0
  181. package/dist/query/unresolved.js.map +1 -0
  182. package/dist/query/verification-status.d.ts +134 -0
  183. package/dist/query/verification-status.d.ts.map +1 -0
  184. package/dist/query/verification-status.js +163 -0
  185. package/dist/query/verification-status.js.map +1 -0
  186. package/dist/recording/index.d.ts +26 -0
  187. package/dist/recording/index.d.ts.map +1 -0
  188. package/dist/recording/index.js +30 -0
  189. package/dist/recording/index.js.map +1 -0
  190. package/dist/recording/migrate.d.ts +22 -0
  191. package/dist/recording/migrate.d.ts.map +1 -0
  192. package/dist/recording/migrate.js +51 -0
  193. package/dist/recording/migrate.js.map +1 -0
  194. package/dist/recording/reader.d.ts +27 -0
  195. package/dist/recording/reader.d.ts.map +1 -0
  196. package/dist/recording/reader.js +55 -0
  197. package/dist/recording/reader.js.map +1 -0
  198. package/dist/recording/redact.d.ts +63 -0
  199. package/dist/recording/redact.d.ts.map +1 -0
  200. package/dist/recording/redact.js +98 -0
  201. package/dist/recording/redact.js.map +1 -0
  202. package/dist/recording/schema.d.ts +41 -0
  203. package/dist/recording/schema.d.ts.map +1 -0
  204. package/dist/recording/schema.js +104 -0
  205. package/dist/recording/schema.js.map +1 -0
  206. package/dist/recording/writer.d.ts +120 -0
  207. package/dist/recording/writer.d.ts.map +1 -0
  208. package/dist/recording/writer.js +81 -0
  209. package/dist/recording/writer.js.map +1 -0
  210. package/dist/scoping/fanout.d.ts +86 -0
  211. package/dist/scoping/fanout.d.ts.map +1 -0
  212. package/dist/scoping/fanout.js +95 -0
  213. package/dist/scoping/fanout.js.map +1 -0
  214. package/dist/scoping/index.d.ts +10 -0
  215. package/dist/scoping/index.d.ts.map +1 -0
  216. package/dist/scoping/index.js +6 -0
  217. package/dist/scoping/index.js.map +1 -0
  218. package/dist/scoping/score.d.ts +83 -0
  219. package/dist/scoping/score.d.ts.map +1 -0
  220. package/dist/scoping/score.js +125 -0
  221. package/dist/scoping/score.js.map +1 -0
  222. package/dist/scoping/tiers.d.ts +32 -0
  223. package/dist/scoping/tiers.d.ts.map +1 -0
  224. package/dist/scoping/tiers.js +35 -0
  225. package/dist/scoping/tiers.js.map +1 -0
  226. package/dist/scoping/traverse.d.ts +60 -0
  227. package/dist/scoping/traverse.d.ts.map +1 -0
  228. package/dist/scoping/traverse.js +172 -0
  229. package/dist/scoping/traverse.js.map +1 -0
  230. package/dist/scoping/weights.d.ts +97 -0
  231. package/dist/scoping/weights.d.ts.map +1 -0
  232. package/dist/scoping/weights.js +167 -0
  233. package/dist/scoping/weights.js.map +1 -0
  234. package/dist/sources/git/diff.d.ts +53 -0
  235. package/dist/sources/git/diff.d.ts.map +1 -0
  236. package/dist/sources/git/diff.js +140 -0
  237. package/dist/sources/git/diff.js.map +1 -0
  238. package/dist/sources/git/env.d.ts +2 -0
  239. package/dist/sources/git/env.d.ts.map +1 -0
  240. package/dist/sources/git/env.js +21 -0
  241. package/dist/sources/git/env.js.map +1 -0
  242. package/dist/sources/git/history.d.ts +140 -0
  243. package/dist/sources/git/history.d.ts.map +1 -0
  244. package/dist/sources/git/history.js +314 -0
  245. package/dist/sources/git/history.js.map +1 -0
  246. package/dist/sources/git/index.d.ts +7 -0
  247. package/dist/sources/git/index.d.ts.map +1 -0
  248. package/dist/sources/git/index.js +4 -0
  249. package/dist/sources/git/index.js.map +1 -0
  250. package/dist/sources/git/source.d.ts +95 -0
  251. package/dist/sources/git/source.d.ts.map +1 -0
  252. package/dist/sources/git/source.js +0 -0
  253. package/dist/sources/git/source.js.map +1 -0
  254. package/dist/sources/migrations/dialects.d.ts +59 -0
  255. package/dist/sources/migrations/dialects.d.ts.map +1 -0
  256. package/dist/sources/migrations/dialects.js +84 -0
  257. package/dist/sources/migrations/dialects.js.map +1 -0
  258. package/dist/sources/migrations/index.d.ts +3 -0
  259. package/dist/sources/migrations/index.d.ts.map +1 -0
  260. package/dist/sources/migrations/index.js +2 -0
  261. package/dist/sources/migrations/index.js.map +1 -0
  262. package/dist/sources/migrations/read.d.ts +113 -0
  263. package/dist/sources/migrations/read.d.ts.map +1 -0
  264. package/dist/sources/migrations/read.js +270 -0
  265. package/dist/sources/migrations/read.js.map +1 -0
  266. package/dist/sources/workspace/index.d.ts +3 -0
  267. package/dist/sources/workspace/index.d.ts.map +1 -0
  268. package/dist/sources/workspace/index.js +2 -0
  269. package/dist/sources/workspace/index.js.map +1 -0
  270. package/dist/sources/workspace/workspace.d.ts +64 -0
  271. package/dist/sources/workspace/workspace.d.ts.map +1 -0
  272. package/dist/sources/workspace/workspace.js +294 -0
  273. package/dist/sources/workspace/workspace.js.map +1 -0
  274. package/dist/store/driver/driver.d.ts +82 -0
  275. package/dist/store/driver/driver.d.ts.map +1 -0
  276. package/dist/store/driver/driver.js +31 -0
  277. package/dist/store/driver/driver.js.map +1 -0
  278. package/dist/store/driver/node-sqlite.d.ts +28 -0
  279. package/dist/store/driver/node-sqlite.d.ts.map +1 -0
  280. package/dist/store/driver/node-sqlite.js +188 -0
  281. package/dist/store/driver/node-sqlite.js.map +1 -0
  282. package/dist/store/index.d.ts +33 -0
  283. package/dist/store/index.d.ts.map +1 -0
  284. package/dist/store/index.js +46 -0
  285. package/dist/store/index.js.map +1 -0
  286. package/dist/store/migrate.d.ts +68 -0
  287. package/dist/store/migrate.d.ts.map +1 -0
  288. package/dist/store/migrate.js +140 -0
  289. package/dist/store/migrate.js.map +1 -0
  290. package/dist/store/reader.d.ts +127 -0
  291. package/dist/store/reader.d.ts.map +1 -0
  292. package/dist/store/reader.js +269 -0
  293. package/dist/store/reader.js.map +1 -0
  294. package/dist/store/schema.d.ts +87 -0
  295. package/dist/store/schema.d.ts.map +1 -0
  296. package/dist/store/schema.js +322 -0
  297. package/dist/store/schema.js.map +1 -0
  298. package/dist/store/writer.d.ts +116 -0
  299. package/dist/store/writer.d.ts.map +1 -0
  300. package/dist/store/writer.js +279 -0
  301. package/dist/store/writer.js.map +1 -0
  302. package/dist/tiers/certify.d.ts +375 -0
  303. package/dist/tiers/certify.d.ts.map +1 -0
  304. package/dist/tiers/certify.js +434 -0
  305. package/dist/tiers/certify.js.map +1 -0
  306. package/dist/tiers/index.d.ts +5 -0
  307. package/dist/tiers/index.d.ts.map +1 -0
  308. package/dist/tiers/index.js +3 -0
  309. package/dist/tiers/index.js.map +1 -0
  310. package/dist/tiers/ladder.d.ts +73 -0
  311. package/dist/tiers/ladder.d.ts.map +1 -0
  312. package/dist/tiers/ladder.js +197 -0
  313. package/dist/tiers/ladder.js.map +1 -0
  314. package/dist/validation/config-graph.d.ts +104 -0
  315. package/dist/validation/config-graph.d.ts.map +1 -0
  316. package/dist/validation/config-graph.js +205 -0
  317. package/dist/validation/config-graph.js.map +1 -0
  318. package/dist/validation/env.d.ts +243 -0
  319. package/dist/validation/env.d.ts.map +1 -0
  320. package/dist/validation/env.js +447 -0
  321. package/dist/validation/env.js.map +1 -0
  322. package/dist/validation/index.d.ts +16 -0
  323. package/dist/validation/index.d.ts.map +1 -0
  324. package/dist/validation/index.js +9 -0
  325. package/dist/validation/index.js.map +1 -0
  326. package/dist/validation/join-substitution.d.ts +186 -0
  327. package/dist/validation/join-substitution.d.ts.map +1 -0
  328. package/dist/validation/join-substitution.js +191 -0
  329. package/dist/validation/join-substitution.js.map +1 -0
  330. package/dist/validation/migrations.d.ts +133 -0
  331. package/dist/validation/migrations.d.ts.map +1 -0
  332. package/dist/validation/migrations.js +315 -0
  333. package/dist/validation/migrations.js.map +1 -0
  334. package/dist/validation/nodes.d.ts +32 -0
  335. package/dist/validation/nodes.d.ts.map +1 -0
  336. package/dist/validation/nodes.js +45 -0
  337. package/dist/validation/nodes.js.map +1 -0
  338. package/dist/validation/rollback.d.ts +124 -0
  339. package/dist/validation/rollback.d.ts.map +1 -0
  340. package/dist/validation/rollback.js +302 -0
  341. package/dist/validation/rollback.js.map +1 -0
  342. package/dist/validation/route-drift.d.ts +108 -0
  343. package/dist/validation/route-drift.d.ts.map +1 -0
  344. package/dist/validation/route-drift.js +103 -0
  345. package/dist/validation/route-drift.js.map +1 -0
  346. package/dist/validation/sources.d.ts +153 -0
  347. package/dist/validation/sources.d.ts.map +1 -0
  348. package/dist/validation/sources.js +409 -0
  349. package/dist/validation/sources.js.map +1 -0
  350. package/dist/worker/index.d.ts +5 -0
  351. package/dist/worker/index.d.ts.map +1 -0
  352. package/dist/worker/index.js +3 -0
  353. package/dist/worker/index.js.map +1 -0
  354. package/dist/worker/pool.d.ts +73 -0
  355. package/dist/worker/pool.d.ts.map +1 -0
  356. package/dist/worker/pool.js +203 -0
  357. package/dist/worker/pool.js.map +1 -0
  358. package/dist/worker/protocol.d.ts +91 -0
  359. package/dist/worker/protocol.d.ts.map +1 -0
  360. package/dist/worker/protocol.js +19 -0
  361. package/dist/worker/protocol.js.map +1 -0
  362. package/dist/worker/traversal.worker.d.ts +25 -0
  363. package/dist/worker/traversal.worker.d.ts.map +1 -0
  364. package/dist/worker/traversal.worker.js +124 -0
  365. package/dist/worker/traversal.worker.js.map +1 -0
  366. package/package.json +29 -0
@@ -0,0 +1,333 @@
1
+ /**
2
+ * §14.4 — is this overlap two people adding lines, or two people disagreeing?
3
+ *
4
+ * `overlap.ts` surfaces a shared node and stops, and says why it stops: *"§14.4's
5
+ * mechanical-versus-semantic classification needs the actual diff lines at the
6
+ * shared node, which this module never sees … Guessing 'mechanical' without
7
+ * reading the lines would be the one place in this system where a wrong guess
8
+ * edits somebody's branch."* This is that module, and it reads the lines.
9
+ *
10
+ * ## The bar here is higher than anywhere else in §14, not lower
11
+ *
12
+ * §14.4 is the **single documented exception** to approval-before-code-change
13
+ * (§9, rule 8). Every other check in this package tells a human to look at
14
+ * something. A `MECHANICAL` verdict may be **auto-applied** — it edits a branch
15
+ * without asking.
16
+ *
17
+ * So rule 2 inverts in consequence, and the asymmetry is the whole design:
18
+ *
19
+ * > A missed mechanical resolution costs a human thirty seconds of `git
20
+ * > mergetool`. A false one silently rewrites two people's work into something
21
+ * > neither wrote and reports success.
22
+ *
23
+ * `MECHANICAL` is therefore claimed **only when every condition is affirmatively
24
+ * established**, never when nothing contradicts it. Absent hunks, a hunk whose
25
+ * shape cannot be read, a file this module cannot reason about — each produces
26
+ * `UNCLASSIFIABLE` with a reason, and `UNCLASSIFIABLE` never auto-applies.
27
+ * `SEMANTIC` is the answer when the lines were read and disagree; that is a
28
+ * different sentence from "the lines were not read", and the two are kept apart
29
+ * for the reason they are kept apart everywhere else in this package.
30
+ *
31
+ * ## What "only add new, non-overlapping lines" has to mean to be safe
32
+ *
33
+ * §14.4's diamond asks whether both changes *"only add new, non-overlapping
34
+ * lines"*. Read literally that is a test on the post-image, and the post-images
35
+ * of two branches are not comparable — they are two different files. The test
36
+ * that means anything is on the **base**:
37
+ *
38
+ * 1. **Every hunk on both sides is a pure insertion** — it consumes zero base
39
+ * lines (`baseLines === 0`). One modified or deleted base line anywhere at
40
+ * this node makes the whole overlap `SEMANTIC`. Not "the modified hunks are
41
+ * semantic and the rest are mechanical": a partially auto-applied overlap is
42
+ * a merge nobody reviewed.
43
+ *
44
+ * 2. **No two insertion points from opposite sides fall within the context
45
+ * window.** Two insertions at base lines 10 and 12 do not touch the same
46
+ * line and git still refuses them, because their three-line contexts
47
+ * overlap. Matching git's own margin rather than testing for exact equality
48
+ * is the difference between a rule about text and a rule about what the
49
+ * merge will actually do.
50
+ *
51
+ * The margin is `CONTEXT_LINES`, and it is deliberately the *conservative*
52
+ * direction: a real mechanical case inside the margin is reported `SEMANTIC` and
53
+ * a human resolves it in half a minute.
54
+ *
55
+ * ## Two claims, two different sources of authority — and only one of them
56
+ * gates the apply
57
+ *
58
+ * This finding rests on two separate things and it is worth being exact about
59
+ * which does what, because conflating them would either block safe applies or
60
+ * permit unsafe ones:
61
+ *
62
+ * | claim | rests on | governs |
63
+ * | --- | --- | --- |
64
+ * | *both changes touch this node* | the graph | whether this is **surfaced**, and the reliability class |
65
+ * | *these hunks merge without conflict* | the diff text | whether this may be **applied** |
66
+ *
67
+ * The reliability class follows `overlap.ts` — the weaker of the two sides'
68
+ * resolutions, per DEC-058 and §11A.5 — and it describes how much to trust that
69
+ * this overlap *matters*. It does not gate the apply, and that is not a
70
+ * loosening: if the graph were wrong about the node, "keep both non-adjacent
71
+ * pure insertions" is still exactly as safe, because the textual claim never
72
+ * depended on the graph being right. Gating the apply on the graph's resolution
73
+ * would be borrowing authority from the wrong evidence.
74
+ *
75
+ * A caller wanting the stricter rule can require class A itself. The point is
76
+ * that the module states which evidence carries which claim rather than folding
77
+ * both into one number.
78
+ *
79
+ * ## Verify-after is not a recommendation and is not expressible as one
80
+ *
81
+ * §14.4 makes the post-apply verification **mandatory** (§19). A `verifyAfter:
82
+ * true` field on a result would be advisory, and an advisory obligation is one
83
+ * `if` away from being skipped by a caller in a hurry.
84
+ *
85
+ * So the apply path is `applyMechanical(plan, { apply, verify })` and **both
86
+ * callbacks are required by the type**. There is no overload that applies
87
+ * without verifying. A `verify` returning false produces `"reverted"`, and a
88
+ * `verify` that throws produces `"verification-failed"` — which is *not* the
89
+ * same outcome and is not collapsed into it, because "the change broke
90
+ * something" and "we could not tell whether the change broke something" are the
91
+ * two categories §20.2 exists to keep apart.
92
+ */
93
+ import { reliabilityCap, reliabilityNote } from "@descryy/ir";
94
+ /**
95
+ * git's default diff context. Two insertions closer than this on the base
96
+ * produce overlapping context and git declines to merge them, so this module
97
+ * declines too.
98
+ */
99
+ export const CONTEXT_LINES = 3;
100
+ /** Every reason this module declines, as data — a refusal is a row, never a silence. */
101
+ const REASONS = {
102
+ noHunksA: "no hunks supplied for side A at this node, so nothing was read",
103
+ noHunksB: "no hunks supplied for side B at this node, so nothing was read",
104
+ noHunksEither: "no hunks supplied for either side at this node, so nothing was read",
105
+ negativeLines: "a hunk reports a negative line count, which this module cannot interpret; " +
106
+ "an unreadable hunk is not an absent one",
107
+ };
108
+ function hunksFor(hunks, node) {
109
+ // A shared node without a file cannot be matched to a hunk. That is a refusal
110
+ // below, not a filter that silently returns nothing here.
111
+ if (node.file === undefined)
112
+ return [];
113
+ return hunks.filter((hunk) => hunk.file === node.file);
114
+ }
115
+ function classifyOne(node, aHunks, bHunks, contextLines) {
116
+ const reliability = node.reliability;
117
+ const disclosure = `${reliabilityNote(node.resolution, false)} This classification's textual half — whether ` +
118
+ "the hunks merge — rests on the diff and not on the graph; the class above describes how " +
119
+ "far to rely on this overlap mattering at all.";
120
+ const base = {
121
+ nodeId: node.nodeId,
122
+ reliability,
123
+ resolution_level: node.resolution,
124
+ hunksRead: [aHunks.length, bHunks.length],
125
+ disclosure,
126
+ };
127
+ if (node.file === undefined) {
128
+ return {
129
+ ...base,
130
+ verdict: "UNCLASSIFIABLE",
131
+ reason: "this shared node carries no file, so its hunks cannot be located. Node identity " +
132
+ "deliberately excludes the path (DEC-004), so a node without a reported file is a " +
133
+ "normal graph state and not a defect — it is simply not classifiable here.",
134
+ };
135
+ }
136
+ if (aHunks.length === 0 && bHunks.length === 0) {
137
+ return { ...base, verdict: "UNCLASSIFIABLE", reason: REASONS.noHunksEither };
138
+ }
139
+ if (aHunks.length === 0)
140
+ return { ...base, verdict: "UNCLASSIFIABLE", reason: REASONS.noHunksA };
141
+ if (bHunks.length === 0)
142
+ return { ...base, verdict: "UNCLASSIFIABLE", reason: REASONS.noHunksB };
143
+ const all = [...aHunks, ...bHunks];
144
+ if (all.some((hunk) => hunk.baseLines < 0 || hunk.headLines < 0 || hunk.baseStart < 0)) {
145
+ return { ...base, verdict: "UNCLASSIFIABLE", reason: REASONS.negativeLines };
146
+ }
147
+ // Condition 1 — every hunk on BOTH sides is a pure insertion. One modified
148
+ // base line anywhere at this node makes the whole overlap semantic; there is
149
+ // no partial auto-apply, because a partially applied overlap is a merge
150
+ // nobody reviewed.
151
+ const modifying = all.filter((hunk) => hunk.baseLines > 0);
152
+ if (modifying.length > 0) {
153
+ const where = modifying
154
+ .slice(0, 3)
155
+ .map((hunk) => `${hunk.file}:${hunk.baseStart}(+${hunk.baseLines} base lines)`)
156
+ .join(", ");
157
+ return {
158
+ ...base,
159
+ verdict: "SEMANTIC",
160
+ reason: `${modifying.length} of ${all.length} hunk(s) modify or delete existing base lines ` +
161
+ `(${where}${modifying.length > 3 ? ", …" : ""}). §14.4 admits only pure additions, and ` +
162
+ "admits them only when every hunk at the node is one — a partially auto-applied " +
163
+ "overlap is a merge nobody reviewed.",
164
+ };
165
+ }
166
+ // Condition 2 — no insertion point from one side within the context window of
167
+ // one from the other. Exact equality is not the test: git conflicts on
168
+ // overlapping context, so this uses git's own margin.
169
+ const collisions = [];
170
+ for (const a of aHunks) {
171
+ for (const b of bHunks) {
172
+ const gap = Math.abs(a.baseStart - b.baseStart);
173
+ if (gap <= contextLines) {
174
+ collisions.push(`${a.file}:${a.baseStart} vs :${b.baseStart} (${gap} line(s) apart)`);
175
+ }
176
+ }
177
+ }
178
+ if (collisions.length > 0) {
179
+ return {
180
+ ...base,
181
+ verdict: "SEMANTIC",
182
+ reason: `both sides insert within ${contextLines} line(s) of each other on the base ` +
183
+ `(${collisions.slice(0, 3).join("; ")}${collisions.length > 3 ? "; …" : ""}). ` +
184
+ "The insertions do not share a line and git still declines them, because their " +
185
+ "contexts overlap — so this module declines too rather than proposing a merge git " +
186
+ "would refuse.",
187
+ };
188
+ }
189
+ const order = [
190
+ ...aHunks.map((hunk) => ({ side: "a", hunk })),
191
+ ...bHunks.map((hunk) => ({ side: "b", hunk })),
192
+ ].sort((x, y) => x.hunk.baseStart - y.hunk.baseStart || x.side.localeCompare(y.side));
193
+ return {
194
+ ...base,
195
+ verdict: "MECHANICAL",
196
+ reason: `all ${all.length} hunk(s) at this node are pure insertions and the two sides' ` +
197
+ `insertion points are more than ${contextLines} line(s) apart on the base, so "keep ` +
198
+ 'both" is a merge git would produce unaided. Auto-applying it is §14.4\'s documented ' +
199
+ "exception to approval-before-code-change, and the verification after it is mandatory.",
200
+ resolution: { kind: "keep-both", order },
201
+ };
202
+ }
203
+ /**
204
+ * Classify every surfaced overlap between two changes.
205
+ *
206
+ * @param shared the nodes `findOverlaps` surfaced — this module does not decide
207
+ * what is worth looking at, only what may be done about it
208
+ * @param aHunks side A's hunks, against the base both sides share
209
+ * @param bHunks side B's hunks, against the same base
210
+ */
211
+ export function classifyOverlaps(shared, aHunks, bHunks, options = {}) {
212
+ const contextLines = options.contextLines ?? CONTEXT_LINES;
213
+ const classifications = shared.map((node) => classifyOne(node, hunksFor(aHunks, node), hunksFor(bHunks, node), contextLines));
214
+ const counts = {
215
+ shared: shared.length,
216
+ withHunksBothSides: classifications.filter((c) => c.hunksRead[0] > 0 && c.hunksRead[1] > 0).length,
217
+ mechanical: classifications.filter((c) => c.verdict === "MECHANICAL").length,
218
+ semantic: classifications.filter((c) => c.verdict === "SEMANTIC").length,
219
+ unclassifiable: classifications.filter((c) => c.verdict === "UNCLASSIFIABLE").length,
220
+ };
221
+ // The baseline precondition, same shape as `hidden-dependency.ts`: nothing was
222
+ // read, so nothing can be concluded — and "nothing was mechanical" is a
223
+ // conclusion.
224
+ const analysed = counts.withHunksBothSides > 0;
225
+ const notes = [];
226
+ if (!analysed && shared.length > 0) {
227
+ notes.push("No shared node had hunks on both sides, so this check did not run. That is not a " +
228
+ "finding that nothing is mechanically resolvable: a caller that did not supply diffs, " +
229
+ "hunks whose paths do not match the nodes' reported files, and an overlap that is " +
230
+ "genuinely all semantic are three different situations and this answer cannot tell " +
231
+ "them apart.");
232
+ }
233
+ if (counts.unclassifiable > 0) {
234
+ notes.push(`${counts.unclassifiable} overlap(s) could not be classified and are NOT semantic — ` +
235
+ "nothing read them. They never auto-apply.");
236
+ }
237
+ if (counts.mechanical > 0) {
238
+ notes.push(`${counts.mechanical} overlap(s) are mechanically resolvable. Applying one is the single ` +
239
+ "documented exception to approval-before-code-change (§9, §14.4) and the verification " +
240
+ "after it is mandatory, not advisory — see applyMechanical().");
241
+ }
242
+ const floor = classifications.reduce((lowest, c) => (c.resolution_level < lowest ? c.resolution_level : lowest), 4);
243
+ const stamp = options.stamp ?? {
244
+ commitSha: null,
245
+ graphBuiltAt: null,
246
+ irSchemaVersion: 1,
247
+ resolutionFloor: shared.length === 0 ? 0 : floor,
248
+ };
249
+ return { data: { classifications, analysed, checked: counts }, stamp, truncated: false, notes };
250
+ }
251
+ /**
252
+ * Apply one mechanical resolution, then verify — in that order, always both.
253
+ *
254
+ * The mandatory verification of §14.4/§19 is enforced by the *type*: `verify` is
255
+ * a required member of `ApplyHooks`, so a caller that wants to skip it has to
256
+ * write a function that lies, which is a different and more visible act than
257
+ * omitting an optional field.
258
+ *
259
+ * Refuses outright, before touching anything, when the verdict is not
260
+ * `MECHANICAL` or when no `revert` was supplied — auto-applying a change that
261
+ * cannot be undone if verification fails is the exception's precondition, not a
262
+ * detail of it.
263
+ */
264
+ export async function applyMechanical(classification, hooks) {
265
+ if (classification.verdict !== "MECHANICAL" || classification.resolution === undefined) {
266
+ return {
267
+ status: "refused",
268
+ why: `verdict is ${classification.verdict}, and only MECHANICAL may auto-apply. ` +
269
+ `Reason given: ${classification.reason}`,
270
+ };
271
+ }
272
+ if (hooks.revert === undefined) {
273
+ return {
274
+ status: "refused",
275
+ why: "no revert hook was supplied. §14.4's exception is auto-apply PLUS mandatory " +
276
+ "verification, and a verification whose failure cannot undo the change is a report, " +
277
+ "not a safeguard.",
278
+ };
279
+ }
280
+ try {
281
+ await hooks.apply(classification.resolution);
282
+ }
283
+ catch (error) {
284
+ return { status: "refused", why: `apply threw and nothing was verified: ${String(error)}` };
285
+ }
286
+ let ok;
287
+ try {
288
+ ok = await hooks.verify();
289
+ }
290
+ catch (error) {
291
+ // Verification could not answer. Revert anyway — the change is live and
292
+ // unverified, which is the state §14.4 exists to prevent — but report that
293
+ // we do not know rather than that it was bad.
294
+ try {
295
+ await hooks.revert();
296
+ }
297
+ catch (revertError) {
298
+ return {
299
+ status: "verification-failed",
300
+ why: `verification threw (${String(error)}) AND the revert threw (${String(revertError)}). ` +
301
+ "The branch is in an applied, unverified state and needs a human.",
302
+ };
303
+ }
304
+ return {
305
+ status: "verification-failed",
306
+ why: `verification threw and the change was reverted: ${String(error)}. This is "we cannot tell", not "it broke".`,
307
+ };
308
+ }
309
+ if (!ok) {
310
+ try {
311
+ await hooks.revert();
312
+ }
313
+ catch (revertError) {
314
+ return {
315
+ status: "verification-failed",
316
+ why: `verification failed and the revert threw (${String(revertError)}). The branch is in ` +
317
+ "an applied, failing state and needs a human.",
318
+ };
319
+ }
320
+ return { status: "reverted", why: "verification failed and the change was reverted." };
321
+ }
322
+ return { status: "applied", verified: true };
323
+ }
324
+ /**
325
+ * The reliability class this module would report for a pair of resolutions.
326
+ *
327
+ * Exported so `overlap.ts`'s rule and this one cannot drift apart silently: both
328
+ * take the weaker of the two sides, per DEC-058.
329
+ */
330
+ export function pairReliability(a, b) {
331
+ return reliabilityCap(a < b ? a : b, false);
332
+ }
333
+ //# sourceMappingURL=mechanical.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mechanical.js","sourceRoot":"","sources":["../../src/multipr/mechanical.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * §14.6 — two open changes each adding a migration from the same parent, caught
3
+ * before the merge that turns it into a broken deploy.
4
+ *
5
+ * ## What this computes, and why it is the real computation rather than a proxy
6
+ *
7
+ * Migrations form a DAG: each revision names its parent(s), and a **head** is a
8
+ * revision nothing else descends from. Every migration runner refuses to run
9
+ * with more than one — Alembic's *"Multiple head revisions are present"* is the
10
+ * canonical spelling, and the equivalent exists in every tool that versions
11
+ * migrations at all.
12
+ *
13
+ * The obvious shortcut is to group migrations by declared parent and flag any
14
+ * parent claimed by two different changes. **That is wrong**, and wrong in the
15
+ * direction that produces false findings: a merge migration declaring two
16
+ * parents exists precisely to resolve such a fork, and the shortcut cannot see
17
+ * it. So this computes heads over the union the way the runner does. The check
18
+ * is only worth its Class A ceiling if it computes what the tool computes.
19
+ *
20
+ * ## Nothing here names a framework, and that is §14.6's own argument
21
+ *
22
+ * §14.6 says the SQL adapter makes this work identically across ORMs and raw-SQL
23
+ * tooling "rather than being written once per framework". So the input is a
24
+ * revision, its declared parents and where it came from — the shape every
25
+ * migration tool has, whatever it calls the fields. Reading `down_revision` out
26
+ * of an Alembic file, `dependencies` out of a Django one or a timestamp chain
27
+ * out of a Rails one is producer work. Rule 1: none of those words appear below
28
+ * this sentence.
29
+ *
30
+ * ## The class, and a disagreement with §15.2 that is already settled
31
+ *
32
+ * §15.2 lists "migration head conflict" as **Class A — deterministically
33
+ * verifiable, a structural fact, not an inference**. That is right about the
34
+ * *inference*: given both parent lists, the conclusion follows with no
35
+ * judgement at all. It is not right unconditionally, because the parent lists
36
+ * still have to be obtained, and reading `down_revision` with a regex is R0
37
+ * evidence however certain the arithmetic on top of it.
38
+ *
39
+ * This is the same disagreement the env/config check hit, and §15.3 already
40
+ * records the resolution: **the Class column is a ceiling, not a floor** —
41
+ * "corrected after the env/config check was built and the two disagreed". So A
42
+ * is what this check reaches when the records come from the framework's own
43
+ * migration history (DEC-115's promotion path, R3), and a source-read set lands
44
+ * where its evidence lands. DEC-058 at the finding level: the claim carries the
45
+ * resolution of its own evidence, not of the run.
46
+ *
47
+ * That is now the second check to disagree with §15.2's column in the same
48
+ * direction, which is worth someone's attention as a pattern rather than as two
49
+ * incidents.
50
+ *
51
+ * ## An unread migration is not a migration with no parents
52
+ *
53
+ * An initial migration genuinely has zero parents. A migration whose parent line
54
+ * could not be read has *unknown* parents. Both are "no parents in the record",
55
+ * and collapsing them silently corrupts the head computation — an unread
56
+ * migration might be exactly the merge that resolves the fork being reported.
57
+ *
58
+ * So `parents` is `readonly string[] | "unknown"` and the empty array cannot be
59
+ * used to mean the second thing. The distinction is made unrepresentable at the
60
+ * type rather than handled downstream, which is the repair
61
+ * `bench/lib/README.md` asks for at construction time rather than afterwards.
62
+ */
63
+ import type { ReliabilityClass, ResolutionLevel } from "@descryy/ir";
64
+ /**
65
+ * One migration a change adds.
66
+ *
67
+ * `parents` is the declared parent revision(s): empty for an initial migration,
68
+ * more than one for a merge migration, and `"unknown"` when the producer could
69
+ * not read them. **Never `[]` for the last case.**
70
+ */
71
+ export interface MigrationRecord {
72
+ readonly prId: string;
73
+ /** The revision identifier this migration declares for itself. */
74
+ readonly revision: string;
75
+ readonly parents: readonly string[] | "unknown";
76
+ readonly file: string;
77
+ /** The level at which this record's parent list was obtained. */
78
+ readonly resolution: ResolutionLevel;
79
+ }
80
+ export interface MigrationHead {
81
+ readonly revision: string;
82
+ readonly prId: string;
83
+ readonly file: string;
84
+ }
85
+ export interface HeadConflict {
86
+ /** The changes whose migrations would each be left as a head. */
87
+ readonly prIds: readonly string[];
88
+ readonly heads: readonly MigrationHead[];
89
+ readonly reliability: ReliabilityClass;
90
+ readonly disclosure: string;
91
+ }
92
+ /** A change whose migration descends from one only another open change adds. */
93
+ export interface CrossPrDependency {
94
+ readonly prId: string;
95
+ readonly revision: string;
96
+ /** The parent revision, and the change that introduces it. */
97
+ readonly dependsOnRevision: string;
98
+ readonly dependsOnPrId: string;
99
+ readonly reliability: ReliabilityClass;
100
+ readonly disclosure: string;
101
+ }
102
+ export interface MigrationHeadReport {
103
+ /**
104
+ * At most one: either the merged set has a single head or it does not. The
105
+ * conflict names every change involved rather than being reported pairwise,
106
+ * because three changes forking from one parent is one problem, not three.
107
+ */
108
+ readonly conflict: HeadConflict | null;
109
+ /**
110
+ * Ordering constraints between changes. Not conflicts — merging in the stated
111
+ * order is fine, and merging in the other order is not.
112
+ */
113
+ readonly dependencies: readonly CrossPrDependency[];
114
+ /** Two changes declaring the same revision identifier. Always a defect. */
115
+ readonly duplicateRevisions: readonly {
116
+ revision: string;
117
+ prIds: readonly string[];
118
+ }[];
119
+ /**
120
+ * False when no change in the set adds a migration at all.
121
+ *
122
+ * **Not the same as "no conflict".** A set with no migrations and a set whose
123
+ * migrations form a clean chain both report `conflict: null`.
124
+ */
125
+ readonly analysed: boolean;
126
+ /**
127
+ * False when any record's parents were unknown, so the DAG read is partial.
128
+ *
129
+ * A finding computed over a partial DAG is conditional on what was not read —
130
+ * an unread migration may be the merge that resolves it — and its class is
131
+ * capped at C for that reason alone, regardless of the resolution of the
132
+ * records that *were* read.
133
+ */
134
+ readonly complete: boolean;
135
+ readonly checked: {
136
+ readonly changes: number;
137
+ readonly migrations: number;
138
+ readonly withUnknownParents: number;
139
+ readonly heads: number;
140
+ };
141
+ readonly notes: readonly string[];
142
+ }
143
+ export interface MigrationHeadOptions {
144
+ /**
145
+ * The head revision of the base branch, when the caller knows it.
146
+ *
147
+ * Supplying it enables one extra check — a migration branching from a
148
+ * revision that is *not* the current head is building on stale history — and
149
+ * its absence is disclosed rather than assumed away.
150
+ */
151
+ readonly baseHead?: string;
152
+ }
153
+ /**
154
+ * Head conflicts across a set of in-flight changes.
155
+ *
156
+ * Pure over records: no store, no graph, no framework. The producer that reads
157
+ * migration files decides `resolution`; this function propagates it.
158
+ */
159
+ export declare function findMigrationHeadConflicts(records: readonly MigrationRecord[], options?: MigrationHeadOptions): MigrationHeadReport;
160
+ //# sourceMappingURL=migration-heads.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-heads.d.ts","sourceRoot":"","sources":["../../src/multipr/migration-heads.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAErE;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IACzC,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,gFAAgF;AAChF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8DAA8D;IAC9D,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IACvC;;;OAGG;IACH,QAAQ,CAAC,YAAY,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACpD,2EAA2E;IAC3E,QAAQ,CAAC,kBAAkB,EAAE,SAAS;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IACvF;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE;QAChB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;QACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,SAAS,eAAe,EAAE,EACnC,OAAO,GAAE,oBAAyB,GACjC,mBAAmB,CAqKrB"}