@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,186 @@
1
+ /**
2
+ * DEC-166 — the join/identity substitution control for `USES_API`.
3
+ *
4
+ * There are two different things called "`USES_API` control" and conflating
5
+ * them is why the row looked unreachable. **Source fidelity** — does a call
6
+ * site's text really support the edge's claim — is
7
+ * `bench/precision-adjudicate.mjs`'s job and is almost entirely
8
+ * `null`-producing (NOTE-C). **Join / identity fidelity** — does the endpoint
9
+ * id a caller's edge targets correspond, and *only* correspond, to the route
10
+ * an independent producer says is served there — is this file's job, and it
11
+ * is the layer the numbers this project publishes are actually made of.
12
+ *
13
+ * ## The instrument
14
+ *
15
+ * For every joined `USES_API` edge:
16
+ *
17
+ * 1. Take the **population** — every `SERVES_API`-linked route in the same
18
+ * repository, deduplicated by (method, normalised template). A fact
19
+ * produced by a *different pass* than the one under test.
20
+ * 2. **Exclude** any route identical in (method, template) to the edge's true
21
+ * target. A group of one substitutes onto itself, which is not a wrong
22
+ * target. This exclusion is load-bearing: without it every draw would match
23
+ * by construction and the control would manufacture false acceptances.
24
+ * 3. Substitute a different route from that population, holding the caller's
25
+ * raw evidence fixed.
26
+ * 4. Re-run the same identity computation against the substituted target.
27
+ * 5. **Expect refusal, always.** The substituted target's identity did not
28
+ * come from the rule under test and did not come from the caller — it came
29
+ * from an independent producer, so the harness can say why the seeded value
30
+ * is wrong without consulting the rule being tested. That is precisely what
31
+ * NOTE-C limit (a) requires and address-keyed source rules could not give.
32
+ *
33
+ * **Threshold 0%, and it is not a dial.** Wrongness here is established by an
34
+ * independent producer, so 0% is the honest floor rather than a number picked
35
+ * to accommodate anything. A non-zero acceptance is a row to read, never a
36
+ * ceiling to raise.
37
+ *
38
+ * ## What this control is, honestly, and what it is not
39
+ *
40
+ * On a correct implementation this control is **structurally** 0%: the
41
+ * caller's evidence normalises onto its own target, the population is
42
+ * deduplicated by that same identity, and every other group is by definition
43
+ * a different string. That is not a weakness dressed up as a floor — it is
44
+ * what a 0% floor *means*, and it is why the number is not a dial. Its value
45
+ * is as a **regression detector over the normalisation layer**: change
46
+ * `normaliseEndpointPath` (or the DEC-097 base composition, or the method
47
+ * half of the identity) so that it discards discriminating information, and
48
+ * two genuinely distinct routes collapse onto one string — at which point the
49
+ * caller's evidence supports a route it never called and the acceptance rate
50
+ * leaves zero. It is a detector for the *class* of suspect join, not a rule
51
+ * that says which flagged rows are wrong; a flagged row still needs a
52
+ * decision, the same way DEC-163's own open question does.
53
+ *
54
+ * ## Why the exclusion key is fixed while the identity under test is not
55
+ *
56
+ * `options.identity` exists so a deliberately degraded identity can be run
57
+ * through the same instrument and the control observed to fire — this repo's
58
+ * standing bar for an instrument (DEC-162's seeded-grader work): a control
59
+ * that has never been seen to fire proves nothing. The population's dedup and
60
+ * step 2's exclusion deliberately do **not** use it. They use the engine's
61
+ * fixed `endpointQsp`, because they are arithmetic over an independently
62
+ * produced route table, not the thing under test. If the exclusion used the
63
+ * identity under test, a degraded identity would collapse the whole
64
+ * population into the target's own group, every candidate would be excluded,
65
+ * and the run would report `UNCONTROLLABLE` — the defect hiding inside the
66
+ * one state that is explicitly not a pass. `join-substitution.test.ts` pins
67
+ * exactly that.
68
+ */
69
+ /**
70
+ * One route from the independently-produced side: a `SERVES_API`-linked
71
+ * route's own declared method and path template.
72
+ */
73
+ export interface SubstitutionRoute {
74
+ readonly method: string;
75
+ readonly path: string;
76
+ /** Reporting only — never part of any key. */
77
+ readonly label?: string;
78
+ }
79
+ /**
80
+ * One joined `USES_API` edge, reduced to the caller's own raw evidence plus
81
+ * the target it actually joined to.
82
+ *
83
+ * `basePath` is DEC-097's base with the origin already stripped and the path
84
+ * prefix kept; `written` is the path exactly as it appears at the call site.
85
+ * The composition is `basePath + written` and nothing else — re-implementing
86
+ * it here is how the instrument and the engine drift into disagreeing about
87
+ * what a join even is.
88
+ */
89
+ export interface SubstitutionCaller {
90
+ /** Where the call site is, for reporting only. */
91
+ readonly at: string;
92
+ readonly method: string;
93
+ readonly basePath: string;
94
+ readonly written: string;
95
+ /** The joined endpoint's own (method, template), as the graph stores it. */
96
+ readonly targetMethod: string;
97
+ readonly targetPath: string;
98
+ }
99
+ /**
100
+ * The three states in which the control has no material to run against.
101
+ *
102
+ * Same third-state discipline as DEC-162: none of these counts toward a
103
+ * numerator or a denominator, and a repository reporting one has said nothing
104
+ * about whether its join logic is sound — it has said it has no material to
105
+ * test it against, which is a fact about the corpus, not the engine.
106
+ */
107
+ export type SubstitutionUncontrollable = {
108
+ readonly code: "POPULATION_ZERO";
109
+ readonly detail: string;
110
+ } | {
111
+ readonly code: "POPULATION_UNDER_TWO";
112
+ readonly detail: string;
113
+ } | {
114
+ readonly code: "NO_JOINED_EDGES";
115
+ readonly detail: string;
116
+ };
117
+ /**
118
+ * A substituted route the caller's own evidence accepted. Must never happen.
119
+ *
120
+ * `trueTarget` and `substituted` are the **fixed** group keys, not the
121
+ * identity under test — a flagged row has to name two routes a human can go
122
+ * and read, and under a degraded identity the string under test is precisely
123
+ * the one that no longer distinguishes them. `collapsedTo` is that string:
124
+ * the value both the caller's evidence and the substituted route produced.
125
+ */
126
+ export interface FlaggedSubstitution {
127
+ readonly at: string;
128
+ /** The route the edge actually joined to, by fixed group key. */
129
+ readonly trueTarget: string;
130
+ /** The independently-real route substituted in, by fixed group key. */
131
+ readonly substituted: string;
132
+ /** The identity string the caller's evidence and the substitute both produced. */
133
+ readonly collapsedTo: string;
134
+ }
135
+ /**
136
+ * A joined edge whose stored target its own raw evidence does not rebuild.
137
+ *
138
+ * Reported separately from an acceptance because it is a different statement:
139
+ * an acceptance says the evidence supports more than one route, this says the
140
+ * evidence does not support the route the edge claims. Both are defects; only
141
+ * the first is what the substitution draw measures.
142
+ */
143
+ export interface UnreproducibleJoin {
144
+ readonly at: string;
145
+ /** What the caller's own raw evidence produced, under the identity in force. */
146
+ readonly derived: string;
147
+ /** The route the edge claims, by fixed group key. */
148
+ readonly trueTarget: string;
149
+ }
150
+ export interface JoinSubstitutionReport {
151
+ /** True only when a substitution was actually drawn. `UNCONTROLLABLE` is never a pass. */
152
+ readonly controllable: boolean;
153
+ readonly uncontrollable?: SubstitutionUncontrollable;
154
+ /** Distinct routes after dedup by (method, normalised template). */
155
+ readonly population: number;
156
+ /** Joined `USES_API` edges considered. */
157
+ readonly callers: number;
158
+ /** The denominator: the sum over callers of that caller's own candidate count. */
159
+ readonly substitutions: number;
160
+ /** The numerator. The floor is 0 and it is not a dial. */
161
+ readonly accepted: number;
162
+ /** `null` rather than 0 when nothing was drawn — 0/0 is not 0%. */
163
+ readonly acceptanceRate: number | null;
164
+ readonly flagged: readonly FlaggedSubstitution[];
165
+ readonly unreproducible: readonly UnreproducibleJoin[];
166
+ }
167
+ export interface JoinSubstitutionOptions {
168
+ /**
169
+ * The identity computation under test. Defaults to the engine's own
170
+ * `endpointQsp`, which is the only value any real run should use — see this
171
+ * file's header for why the seam exists and why the exclusion key does not
172
+ * share it.
173
+ */
174
+ readonly identity?: (method: string, path: string) => string;
175
+ }
176
+ /**
177
+ * Run the control over one repository's joined edges and route table.
178
+ *
179
+ * Deterministic and exhaustive: every candidate in the population is drawn for
180
+ * every caller, never a fixed-N sample. Route tables are frequently far below
181
+ * any fixed draw size — axum shipped 18/18, full population — and a fixed-N
182
+ * bar would make this layer structurally uncontrollable for exactly the
183
+ * repositories with the smallest, most auditable route tables.
184
+ */
185
+ export declare function runJoinSubstitutionControl(callers: readonly SubstitutionCaller[], routes: readonly SubstitutionRoute[], options?: JoinSubstitutionOptions): JoinSubstitutionReport;
186
+ //# sourceMappingURL=join-substitution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"join-substitution.d.ts","sourceRoot":"","sources":["../../src/validation/join-substitution.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AAIH;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,kBAAkB;IACjC,kDAAkD;IAClD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,0BAA0B,GAClC;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC7D;IAAE,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClE;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElE;;;;;;;;GAQG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,kFAAkF;IAClF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,qDAAqD;IACrD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,sBAAsB;IACrC,0FAA0F;IAC1F,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,cAAc,CAAC,EAAE,0BAA0B,CAAC;IACrD,oEAAoE;IACpE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,0CAA0C;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kFAAkF;IAClF,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,mEAAmE;IACnE,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACjD,QAAQ,CAAC,cAAc,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACxD;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAC9D;AAKD;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,SAAS,kBAAkB,EAAE,EACtC,MAAM,EAAE,SAAS,iBAAiB,EAAE,EACpC,OAAO,GAAE,uBAA4B,GACpC,sBAAsB,CAwHxB"}
@@ -0,0 +1,191 @@
1
+ /**
2
+ * DEC-166 — the join/identity substitution control for `USES_API`.
3
+ *
4
+ * There are two different things called "`USES_API` control" and conflating
5
+ * them is why the row looked unreachable. **Source fidelity** — does a call
6
+ * site's text really support the edge's claim — is
7
+ * `bench/precision-adjudicate.mjs`'s job and is almost entirely
8
+ * `null`-producing (NOTE-C). **Join / identity fidelity** — does the endpoint
9
+ * id a caller's edge targets correspond, and *only* correspond, to the route
10
+ * an independent producer says is served there — is this file's job, and it
11
+ * is the layer the numbers this project publishes are actually made of.
12
+ *
13
+ * ## The instrument
14
+ *
15
+ * For every joined `USES_API` edge:
16
+ *
17
+ * 1. Take the **population** — every `SERVES_API`-linked route in the same
18
+ * repository, deduplicated by (method, normalised template). A fact
19
+ * produced by a *different pass* than the one under test.
20
+ * 2. **Exclude** any route identical in (method, template) to the edge's true
21
+ * target. A group of one substitutes onto itself, which is not a wrong
22
+ * target. This exclusion is load-bearing: without it every draw would match
23
+ * by construction and the control would manufacture false acceptances.
24
+ * 3. Substitute a different route from that population, holding the caller's
25
+ * raw evidence fixed.
26
+ * 4. Re-run the same identity computation against the substituted target.
27
+ * 5. **Expect refusal, always.** The substituted target's identity did not
28
+ * come from the rule under test and did not come from the caller — it came
29
+ * from an independent producer, so the harness can say why the seeded value
30
+ * is wrong without consulting the rule being tested. That is precisely what
31
+ * NOTE-C limit (a) requires and address-keyed source rules could not give.
32
+ *
33
+ * **Threshold 0%, and it is not a dial.** Wrongness here is established by an
34
+ * independent producer, so 0% is the honest floor rather than a number picked
35
+ * to accommodate anything. A non-zero acceptance is a row to read, never a
36
+ * ceiling to raise.
37
+ *
38
+ * ## What this control is, honestly, and what it is not
39
+ *
40
+ * On a correct implementation this control is **structurally** 0%: the
41
+ * caller's evidence normalises onto its own target, the population is
42
+ * deduplicated by that same identity, and every other group is by definition
43
+ * a different string. That is not a weakness dressed up as a floor — it is
44
+ * what a 0% floor *means*, and it is why the number is not a dial. Its value
45
+ * is as a **regression detector over the normalisation layer**: change
46
+ * `normaliseEndpointPath` (or the DEC-097 base composition, or the method
47
+ * half of the identity) so that it discards discriminating information, and
48
+ * two genuinely distinct routes collapse onto one string — at which point the
49
+ * caller's evidence supports a route it never called and the acceptance rate
50
+ * leaves zero. It is a detector for the *class* of suspect join, not a rule
51
+ * that says which flagged rows are wrong; a flagged row still needs a
52
+ * decision, the same way DEC-163's own open question does.
53
+ *
54
+ * ## Why the exclusion key is fixed while the identity under test is not
55
+ *
56
+ * `options.identity` exists so a deliberately degraded identity can be run
57
+ * through the same instrument and the control observed to fire — this repo's
58
+ * standing bar for an instrument (DEC-162's seeded-grader work): a control
59
+ * that has never been seen to fire proves nothing. The population's dedup and
60
+ * step 2's exclusion deliberately do **not** use it. They use the engine's
61
+ * fixed `endpointQsp`, because they are arithmetic over an independently
62
+ * produced route table, not the thing under test. If the exclusion used the
63
+ * identity under test, a degraded identity would collapse the whole
64
+ * population into the target's own group, every candidate would be excluded,
65
+ * and the run would report `UNCONTROLLABLE` — the defect hiding inside the
66
+ * one state that is explicitly not a pass. `join-substitution.test.ts` pins
67
+ * exactly that.
68
+ */
69
+ import { endpointQsp } from "@descryy/ir";
70
+ /** The fixed key: the engine's identity, never the one under test. */
71
+ const groupKey = (method, path) => endpointQsp(method, path);
72
+ /**
73
+ * Run the control over one repository's joined edges and route table.
74
+ *
75
+ * Deterministic and exhaustive: every candidate in the population is drawn for
76
+ * every caller, never a fixed-N sample. Route tables are frequently far below
77
+ * any fixed draw size — axum shipped 18/18, full population — and a fixed-N
78
+ * bar would make this layer structurally uncontrollable for exactly the
79
+ * repositories with the smallest, most auditable route tables.
80
+ */
81
+ export function runJoinSubstitutionControl(callers, routes, options = {}) {
82
+ const identity = options.identity ?? endpointQsp;
83
+ // Step 1 — the population, deduplicated by the engine's own identity.
84
+ const population = new Map();
85
+ for (const route of routes) {
86
+ const key = groupKey(route.method, route.path);
87
+ if (!population.has(key))
88
+ population.set(key, route);
89
+ }
90
+ const empty = {
91
+ population: population.size,
92
+ callers: callers.length,
93
+ substitutions: 0,
94
+ accepted: 0,
95
+ acceptanceRate: null,
96
+ flagged: [],
97
+ unreproducible: [],
98
+ };
99
+ // The route legs are checked before the caller leg, in the order DEC-166
100
+ // names them: a repository with neither routes nor callers is first of all a
101
+ // repository that serves nothing.
102
+ if (population.size === 0) {
103
+ return {
104
+ controllable: false,
105
+ uncontrollable: {
106
+ code: "POPULATION_ZERO",
107
+ detail: "no SERVES_API-linked route in this repository — either no route producer ran, or the " +
108
+ "repository is a pure consumer. Nothing independently produced to substitute.",
109
+ },
110
+ ...empty,
111
+ };
112
+ }
113
+ if (population.size < 2) {
114
+ return {
115
+ controllable: false,
116
+ uncontrollable: {
117
+ code: "POPULATION_UNDER_TWO",
118
+ detail: `${population.size} distinct route after dedup by (method, normalised template) — no ` +
119
+ "candidate exists that is not the true target's own group.",
120
+ },
121
+ ...empty,
122
+ };
123
+ }
124
+ if (callers.length === 0) {
125
+ return {
126
+ controllable: false,
127
+ uncontrollable: {
128
+ code: "NO_JOINED_EDGES",
129
+ detail: `${population.size} routes served and zero USES_API edges joined — a pure-provider ` +
130
+ "repository. Nothing to substitute *for*; not a vacuous pass.",
131
+ },
132
+ ...empty,
133
+ };
134
+ }
135
+ const flagged = [];
136
+ const unreproducible = [];
137
+ let substitutions = 0;
138
+ let accepted = 0;
139
+ for (const caller of callers) {
140
+ // The caller's raw evidence, re-composed and re-normalised. DEC-097's
141
+ // composition is `basePath + written`; DEC-170's query strip and DEC-163's
142
+ // method+template identity are inside `identity` itself.
143
+ const derived = identity(caller.method, `${caller.basePath}${caller.written}`);
144
+ const trueIdentity = identity(caller.targetMethod, caller.targetPath);
145
+ // Step 2 — exclude the true target's own group, under the fixed key.
146
+ const excluded = groupKey(caller.targetMethod, caller.targetPath);
147
+ if (derived !== trueIdentity) {
148
+ unreproducible.push({ at: caller.at, derived, trueTarget: excluded });
149
+ }
150
+ for (const [key, route] of population) {
151
+ if (key === excluded)
152
+ continue;
153
+ substitutions += 1;
154
+ // Steps 3-5 — hold the caller's evidence fixed, re-run the identity
155
+ // against the substituted target, expect refusal.
156
+ if (identity(route.method, route.path) === derived) {
157
+ accepted += 1;
158
+ flagged.push({
159
+ at: caller.at,
160
+ trueTarget: excluded,
161
+ substituted: key,
162
+ collapsedTo: derived,
163
+ });
164
+ }
165
+ }
166
+ }
167
+ return {
168
+ // `population >= 2` guarantees every caller keeps at least one candidate —
169
+ // the target's group is at most one of them — so `substitutions > 0` here
170
+ // is structural rather than hoped for. Asserted through the returned flag
171
+ // anyway, so a future change to the population arithmetic cannot turn an
172
+ // empty draw into a silent pass.
173
+ controllable: substitutions > 0,
174
+ ...(substitutions > 0
175
+ ? {}
176
+ : {
177
+ uncontrollable: {
178
+ code: "POPULATION_UNDER_TWO",
179
+ detail: "every candidate was excluded as the true target's own group",
180
+ },
181
+ }),
182
+ population: population.size,
183
+ callers: callers.length,
184
+ substitutions,
185
+ accepted,
186
+ acceptanceRate: substitutions === 0 ? null : accepted / substitutions,
187
+ flagged,
188
+ unreproducible,
189
+ };
190
+ }
191
+ //# sourceMappingURL=join-substitution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"join-substitution.js","sourceRoot":"","sources":["../../src/validation/join-substitution.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * The format half of §15.3's rollback-readiness check — migrations read from the
3
+ * repository as text.
4
+ *
5
+ * Same argument as `sources.ts`: DDL is a **format, not a language**, so reading
6
+ * it names nothing the Canonical IR boundary forbids. Same limit, too — this half
7
+ * sees only what is written as DDL, and DEC-122 measured exactly how little that
8
+ * is: **zero `.sql` files inside a migration directory across fifteen reference
9
+ * repositories**, against 3,301 migrations written as program code. The code half
10
+ * arrives through `attrs.migrationOps` and is an adapter's to produce.
11
+ *
12
+ * ## The scope fence, which is what makes this Class A
13
+ *
14
+ * Appendix B.3 settles rollback readiness as Class A against two sources that
15
+ * called it B, and the resolution turns entirely on what is claimed:
16
+ *
17
+ * > **"this migration contains a destructive operation"** — a fact, and a
18
+ * > deterministic pattern match establishes it.
19
+ * >
20
+ * > **"this migration is unsafe to roll back"** — an inference, requiring the
21
+ * > consumer-impact analysis §15.3 explicitly separates this check from.
22
+ *
23
+ * Every sentence produced here is written to stay behind that fence. The finding
24
+ * names the operation, the object and the line; it does not characterise risk.
25
+ *
26
+ * ## What counts as destructive, and the things deliberately left out
27
+ *
28
+ * Only operations that **destroy data**. `DROP INDEX`, `DROP CONSTRAINT`,
29
+ * `DROP VIEW`, `DROP TRIGGER` and `DROP FUNCTION` are all recoverable by
30
+ * re-running the definition and none of them loses a row, so including them
31
+ * would triple the finding count with statements nobody needs to see. A rename
32
+ * is carried separately: the data survives, so it is not the same claim as a
33
+ * drop, but every reader of the old name breaks.
34
+ *
35
+ * ## Section detection is tool-blind on purpose
36
+ *
37
+ * Directional migrations mark their halves with a comment directive. Matching a
38
+ * specific tool's spelling would put that tool's name in engine code — rule 1,
39
+ * the violation the boundary lint already caught once in this directory. So the
40
+ * **shape** is matched instead: a comment carrying a directive word followed by
41
+ * an up/down keyword, in either the `+name Up` form or a bare `-- Up`. Any tool
42
+ * using that shape works; none of them is named.
43
+ */
44
+ /**
45
+ * What an operation does to data.
46
+ *
47
+ * `RENAME` is separate because it is a different claim: the rows survive, and
48
+ * what breaks is every reader of the old name.
49
+ */
50
+ export type DestructiveKind = "DROP_TABLE" | "DROP_COLUMN" | "DROP_SCHEMA" | "TRUNCATE" | "RENAME";
51
+ /**
52
+ * Which half of a directional migration a statement sits in.
53
+ *
54
+ * `reverse` is the whole reason this field exists — a drop there undoes the
55
+ * forward migration and is supposed to be present. `unknown` is not a synonym
56
+ * for `forward`: it means the file declares no direction, and DEC-122 refuses to
57
+ * report on it.
58
+ */
59
+ export type MigrationSection = "forward" | "reverse" | "unknown";
60
+ export interface MigrationOp {
61
+ readonly kind: DestructiveKind;
62
+ /** The table, column or schema named by the statement, as written. */
63
+ readonly target: string;
64
+ readonly line: number;
65
+ readonly section: MigrationSection;
66
+ /** `DROP … IF EXISTS` — an idempotent guard rather than an intentional drop. */
67
+ readonly guarded: boolean;
68
+ }
69
+ export interface MigrationFile {
70
+ readonly file: string;
71
+ readonly ops: readonly MigrationOp[];
72
+ /**
73
+ * Objects this file creates, with the line, so a drop of one of them can be
74
+ * discounted — and so the *order* is known. Create-then-drop is a temporary
75
+ * object; drop-then-create is an idempotent setup script. Different reasons,
76
+ * and without the line they are indistinguishable.
77
+ */
78
+ readonly creates: readonly {
79
+ readonly name: string;
80
+ readonly line: number;
81
+ }[];
82
+ /** True when the file marks a forward/reverse split at all. */
83
+ readonly directional: boolean;
84
+ }
85
+ export interface MigrationScan {
86
+ readonly files: readonly MigrationFile[];
87
+ readonly unreadable: readonly string[];
88
+ readonly truncated: boolean;
89
+ /** Files that looked like migrations and held no recognised statement. */
90
+ readonly unparsed: number;
91
+ }
92
+ export interface MigrationScanOptions {
93
+ readonly maxDepth?: number;
94
+ readonly maxFiles?: number;
95
+ /**
96
+ * The active adapters' declared noise directories (DEC-110). Shared with the
97
+ * config-surface walk through `skipDirectory`, so the two cannot disagree about
98
+ * what counts as vendored.
99
+ */
100
+ readonly noiseDirectories?: readonly string[];
101
+ }
102
+ interface Statement {
103
+ readonly text: string;
104
+ readonly line: number;
105
+ }
106
+ /**
107
+ * Split DDL on `;`, ignoring separators inside strings, quoted identifiers and
108
+ * comments.
109
+ *
110
+ * A plain split would cut a statement in half at the first semicolon inside a
111
+ * default value, which is a real thing to find in a migration.
112
+ *
113
+ * **This duplicates the splitter in `adapter-sql`, deliberately and with the
114
+ * cost recorded** (DEC-122). `@descryy/core` cannot depend on an adapter —
115
+ * adapters depend on core — so the alternative to a second copy is inverting the
116
+ * boundary the repository split exists to hold. If a third caller appears, the
117
+ * right home is `@descryy/ir`.
118
+ */
119
+ export declare function statements(sql: string): readonly Statement[];
120
+ /**
121
+ * Classify one statement.
122
+ *
123
+ * Returns `null` for everything unrecognised, which is the overwhelming
124
+ * majority and is correct: an unclassified statement is not a silent pass, it is
125
+ * counted by the caller and disclosed.
126
+ */
127
+ export declare function classifyStatement(text: string, line: number, section: MigrationSection): MigrationOp | null;
128
+ /** Read one migration file's operations, tracking which section each sits in. */
129
+ export declare function readMigration(text: string): Omit<MigrationFile, "file">;
130
+ /** Walk the repository for migration files written as DDL. */
131
+ export declare function scanMigrations(root: string, options?: MigrationScanOptions): MigrationScan;
132
+ export {};
133
+ //# sourceMappingURL=migrations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/validation/migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAOH;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GACvB,YAAY,GACZ,aAAa,GACb,aAAa,GACb,UAAU,GACV,QAAQ,CAAC;AAEb;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAEjE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,SAAS,WAAW,EAAE,CAAC;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9E,+DAA+D;IAC/D,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IACzC,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/C;AAkBD,UAAU,SAAS;IACjB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,SAAS,EAAE,CA6D5D;AAMD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,gBAAgB,GACxB,WAAW,GAAG,IAAI,CA2DpB;AAYD,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAwCvE;AAED,8DAA8D;AAC9D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,aAAa,CA6D9F"}