@descryy/core 0.3.0 → 0.5.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 (362) hide show
  1. package/dist/budget/reply.d.ts +11 -46
  2. package/dist/budget/reply.d.ts.map +1 -1
  3. package/dist/budget/reply.js +10 -39
  4. package/dist/budget/reply.js.map +1 -1
  5. package/dist/budget/size.d.ts +2 -21
  6. package/dist/budget/size.d.ts.map +1 -1
  7. package/dist/budget/size.js +3 -24
  8. package/dist/budget/size.js.map +1 -1
  9. package/dist/capabilities/preconditions.d.ts +7 -68
  10. package/dist/capabilities/preconditions.d.ts.map +1 -1
  11. package/dist/capabilities/preconditions.js +6 -57
  12. package/dist/capabilities/preconditions.js.map +1 -1
  13. package/dist/contracts/engine.d.ts +26 -12
  14. package/dist/contracts/engine.d.ts.map +1 -1
  15. package/dist/contracts/engine.js +28 -11
  16. package/dist/contracts/engine.js.map +1 -1
  17. package/dist/contracts/index.d.ts +1 -1
  18. package/dist/contracts/index.d.ts.map +1 -1
  19. package/dist/contracts/index.js.map +1 -1
  20. package/dist/contracts/orphans.d.ts +28 -77
  21. package/dist/contracts/orphans.d.ts.map +1 -1
  22. package/dist/contracts/orphans.js +124 -94
  23. package/dist/contracts/orphans.js.map +1 -1
  24. package/dist/contracts/paths.d.ts +4 -114
  25. package/dist/contracts/paths.d.ts.map +1 -1
  26. package/dist/contracts/paths.js +7 -140
  27. package/dist/contracts/paths.js.map +1 -1
  28. package/dist/contracts/shapes.d.ts +13 -66
  29. package/dist/contracts/shapes.d.ts.map +1 -1
  30. package/dist/contracts/shapes.js +20 -91
  31. package/dist/contracts/shapes.js.map +1 -1
  32. package/dist/governance/budget.d.ts +12 -48
  33. package/dist/governance/budget.d.ts.map +1 -1
  34. package/dist/governance/budget.js +12 -48
  35. package/dist/governance/budget.js.map +1 -1
  36. package/dist/governance/candidate-boundary.d.ts +4 -32
  37. package/dist/governance/candidate-boundary.d.ts.map +1 -1
  38. package/dist/governance/candidate-boundary.js +4 -32
  39. package/dist/governance/candidate-boundary.js.map +1 -1
  40. package/dist/governance/escalation.d.ts +7 -45
  41. package/dist/governance/escalation.d.ts.map +1 -1
  42. package/dist/governance/escalation.js +7 -45
  43. package/dist/governance/escalation.js.map +1 -1
  44. package/dist/governance/fact-boundary.d.ts +7 -49
  45. package/dist/governance/fact-boundary.d.ts.map +1 -1
  46. package/dist/governance/fact-boundary.js +5 -43
  47. package/dist/governance/fact-boundary.js.map +1 -1
  48. package/dist/governance/finding-funnel.d.ts +11 -72
  49. package/dist/governance/finding-funnel.d.ts.map +1 -1
  50. package/dist/governance/finding-funnel.js +9 -68
  51. package/dist/governance/finding-funnel.js.map +1 -1
  52. package/dist/graph/build.d.ts +13 -75
  53. package/dist/graph/build.d.ts.map +1 -1
  54. package/dist/graph/build.js +0 -0
  55. package/dist/graph/build.js.map +1 -1
  56. package/dist/graph/cross-language.d.ts +4 -43
  57. package/dist/graph/cross-language.d.ts.map +1 -1
  58. package/dist/graph/cross-language.js +9 -77
  59. package/dist/graph/cross-language.js.map +1 -1
  60. package/dist/graph/index.d.ts +1 -1
  61. package/dist/graph/index.d.ts.map +1 -1
  62. package/dist/graph/merge.d.ts +8 -76
  63. package/dist/graph/merge.d.ts.map +1 -1
  64. package/dist/graph/merge.js +27 -77
  65. package/dist/graph/merge.js.map +1 -1
  66. package/dist/graph/observed-tests.d.ts +7 -68
  67. package/dist/graph/observed-tests.d.ts.map +1 -1
  68. package/dist/graph/observed-tests.js +5 -60
  69. package/dist/graph/observed-tests.js.map +1 -1
  70. package/dist/graph/persist.d.ts +2 -25
  71. package/dist/graph/persist.d.ts.map +1 -1
  72. package/dist/graph/persist.js +2 -25
  73. package/dist/graph/persist.js.map +1 -1
  74. package/dist/graph/runtime-confirmation.d.ts +14 -91
  75. package/dist/graph/runtime-confirmation.d.ts.map +1 -1
  76. package/dist/graph/runtime-confirmation.js +19 -103
  77. package/dist/graph/runtime-confirmation.js.map +1 -1
  78. package/dist/ledger/mint.d.ts +9 -43
  79. package/dist/ledger/mint.d.ts.map +1 -1
  80. package/dist/ledger/mint.js +15 -65
  81. package/dist/ledger/mint.js.map +1 -1
  82. package/dist/ledger/resolve.d.ts +13 -81
  83. package/dist/ledger/resolve.d.ts.map +1 -1
  84. package/dist/ledger/resolve.js +13 -76
  85. package/dist/ledger/resolve.js.map +1 -1
  86. package/dist/multipr/fingerprint.d.ts +5 -46
  87. package/dist/multipr/fingerprint.d.ts.map +1 -1
  88. package/dist/multipr/fingerprint.js +22 -74
  89. package/dist/multipr/fingerprint.js.map +1 -1
  90. package/dist/multipr/hidden-dependency.d.ts +13 -73
  91. package/dist/multipr/hidden-dependency.d.ts.map +1 -1
  92. package/dist/multipr/hidden-dependency.js +12 -63
  93. package/dist/multipr/hidden-dependency.js.map +1 -1
  94. package/dist/multipr/mechanical.d.ts +25 -160
  95. package/dist/multipr/mechanical.d.ts.map +1 -1
  96. package/dist/multipr/mechanical.js +21 -137
  97. package/dist/multipr/mechanical.js.map +1 -1
  98. package/dist/multipr/migration-heads.d.ts +18 -105
  99. package/dist/multipr/migration-heads.d.ts.map +1 -1
  100. package/dist/multipr/migration-heads.js +15 -84
  101. package/dist/multipr/migration-heads.js.map +1 -1
  102. package/dist/multipr/overlap.d.ts +16 -94
  103. package/dist/multipr/overlap.d.ts.map +1 -1
  104. package/dist/multipr/overlap.js +25 -105
  105. package/dist/multipr/overlap.js.map +1 -1
  106. package/dist/multipr/scope-store.d.ts +15 -80
  107. package/dist/multipr/scope-store.d.ts.map +1 -1
  108. package/dist/multipr/scope-store.js +17 -87
  109. package/dist/multipr/scope-store.js.map +1 -1
  110. package/dist/multipr/superseded.d.ts +10 -95
  111. package/dist/multipr/superseded.d.ts.map +1 -1
  112. package/dist/multipr/superseded.js +7 -60
  113. package/dist/multipr/superseded.js.map +1 -1
  114. package/dist/query/confirmed-facts.d.ts +12 -58
  115. package/dist/query/confirmed-facts.d.ts.map +1 -1
  116. package/dist/query/confirmed-facts.js +8 -47
  117. package/dist/query/confirmed-facts.js.map +1 -1
  118. package/dist/query/declared-value-closure.d.ts +13 -104
  119. package/dist/query/declared-value-closure.d.ts.map +1 -1
  120. package/dist/query/declared-value-closure.js +15 -104
  121. package/dist/query/declared-value-closure.js.map +1 -1
  122. package/dist/query/memory.d.ts +5 -9
  123. package/dist/query/memory.d.ts.map +1 -1
  124. package/dist/query/memory.js +31 -15
  125. package/dist/query/memory.js.map +1 -1
  126. package/dist/query/prominence.d.ts +6 -55
  127. package/dist/query/prominence.d.ts.map +1 -1
  128. package/dist/query/prominence.js +6 -55
  129. package/dist/query/prominence.js.map +1 -1
  130. package/dist/query/provider.d.ts +28 -81
  131. package/dist/query/provider.d.ts.map +1 -1
  132. package/dist/query/provider.js +8 -29
  133. package/dist/query/provider.js.map +1 -1
  134. package/dist/query/queries.d.ts +15 -65
  135. package/dist/query/queries.d.ts.map +1 -1
  136. package/dist/query/queries.js +43 -100
  137. package/dist/query/queries.js.map +1 -1
  138. package/dist/query/refusal-fetch.d.ts +8 -85
  139. package/dist/query/refusal-fetch.d.ts.map +1 -1
  140. package/dist/query/refusal-fetch.js +11 -93
  141. package/dist/query/refusal-fetch.js.map +1 -1
  142. package/dist/query/refusal-questions.d.ts +17 -92
  143. package/dist/query/refusal-questions.d.ts.map +1 -1
  144. package/dist/query/refusal-questions.js +14 -78
  145. package/dist/query/refusal-questions.js.map +1 -1
  146. package/dist/query/resolution-floor.d.ts +3 -23
  147. package/dist/query/resolution-floor.d.ts.map +1 -1
  148. package/dist/query/resolution-floor.js +3 -23
  149. package/dist/query/resolution-floor.js.map +1 -1
  150. package/dist/query/root-cause-score.d.ts +11 -161
  151. package/dist/query/root-cause-score.d.ts.map +1 -1
  152. package/dist/query/root-cause-score.js +6 -137
  153. package/dist/query/root-cause-score.js.map +1 -1
  154. package/dist/query/row-closure-picture.d.ts +5 -67
  155. package/dist/query/row-closure-picture.d.ts.map +1 -1
  156. package/dist/query/row-closure-picture.js +6 -67
  157. package/dist/query/row-closure-picture.js.map +1 -1
  158. package/dist/query/similar-incidents.d.ts +6 -41
  159. package/dist/query/similar-incidents.d.ts.map +1 -1
  160. package/dist/query/similar-incidents.js +15 -69
  161. package/dist/query/similar-incidents.js.map +1 -1
  162. package/dist/query/sqlite.d.ts +3 -7
  163. package/dist/query/sqlite.d.ts.map +1 -1
  164. package/dist/query/sqlite.js +13 -16
  165. package/dist/query/sqlite.js.map +1 -1
  166. package/dist/query/test-coverage.d.ts +5 -45
  167. package/dist/query/test-coverage.d.ts.map +1 -1
  168. package/dist/query/test-coverage.js +10 -53
  169. package/dist/query/test-coverage.js.map +1 -1
  170. package/dist/query/traverse.d.ts +22 -126
  171. package/dist/query/traverse.d.ts.map +1 -1
  172. package/dist/query/traverse.js +0 -0
  173. package/dist/query/traverse.js.map +1 -1
  174. package/dist/query/unresolved.d.ts +19 -164
  175. package/dist/query/unresolved.d.ts.map +1 -1
  176. package/dist/query/unresolved.js +11 -138
  177. package/dist/query/unresolved.js.map +1 -1
  178. package/dist/query/verification-status.d.ts +12 -109
  179. package/dist/query/verification-status.d.ts.map +1 -1
  180. package/dist/query/verification-status.js +9 -99
  181. package/dist/query/verification-status.js.map +1 -1
  182. package/dist/recording/migrate.d.ts +4 -10
  183. package/dist/recording/migrate.d.ts.map +1 -1
  184. package/dist/recording/migrate.js +4 -10
  185. package/dist/recording/migrate.js.map +1 -1
  186. package/dist/recording/reader.d.ts +2 -7
  187. package/dist/recording/reader.d.ts.map +1 -1
  188. package/dist/recording/reader.js +2 -7
  189. package/dist/recording/reader.js.map +1 -1
  190. package/dist/recording/redact.d.ts +6 -57
  191. package/dist/recording/redact.d.ts.map +1 -1
  192. package/dist/recording/redact.js +8 -64
  193. package/dist/recording/redact.js.map +1 -1
  194. package/dist/recording/schema.d.ts +3 -38
  195. package/dist/recording/schema.d.ts.map +1 -1
  196. package/dist/recording/schema.js +3 -38
  197. package/dist/recording/schema.js.map +1 -1
  198. package/dist/recording/writer.d.ts +11 -74
  199. package/dist/recording/writer.d.ts.map +1 -1
  200. package/dist/recording/writer.js +3 -43
  201. package/dist/recording/writer.js.map +1 -1
  202. package/dist/scoping/fanout.d.ts +6 -68
  203. package/dist/scoping/fanout.d.ts.map +1 -1
  204. package/dist/scoping/fanout.js +6 -68
  205. package/dist/scoping/fanout.js.map +1 -1
  206. package/dist/scoping/score.d.ts +20 -70
  207. package/dist/scoping/score.d.ts.map +1 -1
  208. package/dist/scoping/score.js +20 -70
  209. package/dist/scoping/score.js.map +1 -1
  210. package/dist/scoping/tiers.d.ts +4 -16
  211. package/dist/scoping/tiers.d.ts.map +1 -1
  212. package/dist/scoping/tiers.js +4 -16
  213. package/dist/scoping/tiers.js.map +1 -1
  214. package/dist/scoping/traverse.d.ts +3 -25
  215. package/dist/scoping/traverse.d.ts.map +1 -1
  216. package/dist/scoping/traverse.js +14 -49
  217. package/dist/scoping/traverse.js.map +1 -1
  218. package/dist/scoping/weights.d.ts +10 -74
  219. package/dist/scoping/weights.d.ts.map +1 -1
  220. package/dist/scoping/weights.js +14 -86
  221. package/dist/scoping/weights.js.map +1 -1
  222. package/dist/sources/confirmed/incidents.d.ts +5 -35
  223. package/dist/sources/confirmed/incidents.d.ts.map +1 -1
  224. package/dist/sources/confirmed/incidents.js +14 -48
  225. package/dist/sources/confirmed/incidents.js.map +1 -1
  226. package/dist/sources/git/diff.d.ts +71 -65
  227. package/dist/sources/git/diff.d.ts.map +1 -1
  228. package/dist/sources/git/diff.js +136 -83
  229. package/dist/sources/git/diff.js.map +1 -1
  230. package/dist/sources/git/env.d.ts +7 -0
  231. package/dist/sources/git/env.d.ts.map +1 -1
  232. package/dist/sources/git/env.js +10 -11
  233. package/dist/sources/git/env.js.map +1 -1
  234. package/dist/sources/git/history.d.ts +64 -84
  235. package/dist/sources/git/history.d.ts.map +1 -1
  236. package/dist/sources/git/history.js +141 -112
  237. package/dist/sources/git/history.js.map +1 -1
  238. package/dist/sources/git/incidents.d.ts +13 -75
  239. package/dist/sources/git/incidents.d.ts.map +1 -1
  240. package/dist/sources/git/incidents.js +21 -91
  241. package/dist/sources/git/incidents.js.map +1 -1
  242. package/dist/sources/git/index.d.ts +5 -3
  243. package/dist/sources/git/index.d.ts.map +1 -1
  244. package/dist/sources/git/index.js +3 -2
  245. package/dist/sources/git/index.js.map +1 -1
  246. package/dist/sources/git/source.d.ts +9 -79
  247. package/dist/sources/git/source.d.ts.map +1 -1
  248. package/dist/sources/git/source.js +0 -0
  249. package/dist/sources/git/source.js.map +1 -1
  250. package/dist/sources/migrations/across-change.d.ts +94 -0
  251. package/dist/sources/migrations/across-change.d.ts.map +1 -0
  252. package/dist/sources/migrations/across-change.js +278 -0
  253. package/dist/sources/migrations/across-change.js.map +1 -0
  254. package/dist/sources/migrations/dialects.d.ts +20 -36
  255. package/dist/sources/migrations/dialects.d.ts.map +1 -1
  256. package/dist/sources/migrations/dialects.js +11 -36
  257. package/dist/sources/migrations/dialects.js.map +1 -1
  258. package/dist/sources/migrations/index.d.ts +3 -1
  259. package/dist/sources/migrations/index.d.ts.map +1 -1
  260. package/dist/sources/migrations/index.js +2 -1
  261. package/dist/sources/migrations/index.js.map +1 -1
  262. package/dist/sources/migrations/read.d.ts +14 -79
  263. package/dist/sources/migrations/read.d.ts.map +1 -1
  264. package/dist/sources/migrations/read.js +14 -89
  265. package/dist/sources/migrations/read.js.map +1 -1
  266. package/dist/sources/workspace/workspace.d.ts +6 -42
  267. package/dist/sources/workspace/workspace.d.ts.map +1 -1
  268. package/dist/sources/workspace/workspace.js +10 -53
  269. package/dist/sources/workspace/workspace.js.map +1 -1
  270. package/dist/store/driver/driver.d.ts +9 -38
  271. package/dist/store/driver/driver.d.ts.map +1 -1
  272. package/dist/store/driver/driver.js +4 -22
  273. package/dist/store/driver/driver.js.map +1 -1
  274. package/dist/store/driver/node-sqlite.d.ts +3 -12
  275. package/dist/store/driver/node-sqlite.d.ts.map +1 -1
  276. package/dist/store/driver/node-sqlite.js +4 -18
  277. package/dist/store/driver/node-sqlite.js.map +1 -1
  278. package/dist/store/index.d.ts +2 -7
  279. package/dist/store/index.d.ts.map +1 -1
  280. package/dist/store/index.js +4 -10
  281. package/dist/store/index.js.map +1 -1
  282. package/dist/store/migrate.d.ts +14 -50
  283. package/dist/store/migrate.d.ts.map +1 -1
  284. package/dist/store/migrate.js +15 -52
  285. package/dist/store/migrate.js.map +1 -1
  286. package/dist/store/reader.d.ts +19 -84
  287. package/dist/store/reader.d.ts.map +1 -1
  288. package/dist/store/reader.js +25 -92
  289. package/dist/store/reader.js.map +1 -1
  290. package/dist/store/schema.d.ts +14 -76
  291. package/dist/store/schema.d.ts.map +1 -1
  292. package/dist/store/schema.js +14 -76
  293. package/dist/store/schema.js.map +1 -1
  294. package/dist/store/writer.d.ts +14 -88
  295. package/dist/store/writer.d.ts.map +1 -1
  296. package/dist/store/writer.js +29 -123
  297. package/dist/store/writer.js.map +1 -1
  298. package/dist/tiers/certify.d.ts +46 -281
  299. package/dist/tiers/certify.d.ts.map +1 -1
  300. package/dist/tiers/certify.js +46 -226
  301. package/dist/tiers/certify.js.map +1 -1
  302. package/dist/tiers/ladder.d.ts +7 -51
  303. package/dist/tiers/ladder.d.ts.map +1 -1
  304. package/dist/tiers/ladder.js +13 -89
  305. package/dist/tiers/ladder.js.map +1 -1
  306. package/dist/validation/attributes.d.ts +47 -0
  307. package/dist/validation/attributes.d.ts.map +1 -0
  308. package/dist/validation/attributes.js +130 -0
  309. package/dist/validation/attributes.js.map +1 -0
  310. package/dist/validation/config-graph.d.ts +7 -56
  311. package/dist/validation/config-graph.d.ts.map +1 -1
  312. package/dist/validation/config-graph.js +15 -61
  313. package/dist/validation/config-graph.js.map +1 -1
  314. package/dist/validation/env.d.ts +53 -2
  315. package/dist/validation/env.d.ts.map +1 -1
  316. package/dist/validation/env.js +99 -4
  317. package/dist/validation/env.js.map +1 -1
  318. package/dist/validation/index.d.ts +5 -1
  319. package/dist/validation/index.d.ts.map +1 -1
  320. package/dist/validation/index.js +3 -1
  321. package/dist/validation/index.js.map +1 -1
  322. package/dist/validation/join-substitution.d.ts +22 -120
  323. package/dist/validation/join-substitution.d.ts.map +1 -1
  324. package/dist/validation/join-substitution.js +13 -90
  325. package/dist/validation/join-substitution.js.map +1 -1
  326. package/dist/validation/migration-integrity.d.ts +72 -0
  327. package/dist/validation/migration-integrity.d.ts.map +1 -0
  328. package/dist/validation/migration-integrity.js +238 -0
  329. package/dist/validation/migration-integrity.js.map +1 -0
  330. package/dist/validation/migrations.d.ts +23 -88
  331. package/dist/validation/migrations.d.ts.map +1 -1
  332. package/dist/validation/migrations.js +20 -79
  333. package/dist/validation/migrations.js.map +1 -1
  334. package/dist/validation/nodes.d.ts +3 -1
  335. package/dist/validation/nodes.d.ts.map +1 -1
  336. package/dist/validation/nodes.js +3 -1
  337. package/dist/validation/nodes.js.map +1 -1
  338. package/dist/validation/rollback.d.ts +10 -73
  339. package/dist/validation/rollback.d.ts.map +1 -1
  340. package/dist/validation/rollback.js +23 -81
  341. package/dist/validation/rollback.js.map +1 -1
  342. package/dist/validation/route-drift.d.ts +9 -62
  343. package/dist/validation/route-drift.d.ts.map +1 -1
  344. package/dist/validation/route-drift.js +7 -52
  345. package/dist/validation/route-drift.js.map +1 -1
  346. package/dist/validation/sources.d.ts +16 -94
  347. package/dist/validation/sources.d.ts.map +1 -1
  348. package/dist/validation/sources.js +51 -147
  349. package/dist/validation/sources.js.map +1 -1
  350. package/dist/worker/pool.d.ts +6 -45
  351. package/dist/worker/pool.d.ts.map +1 -1
  352. package/dist/worker/pool.js +5 -42
  353. package/dist/worker/pool.js.map +1 -1
  354. package/dist/worker/protocol.d.ts +7 -20
  355. package/dist/worker/protocol.d.ts.map +1 -1
  356. package/dist/worker/protocol.js +3 -10
  357. package/dist/worker/protocol.js.map +1 -1
  358. package/dist/worker/traversal.worker.d.ts +2 -21
  359. package/dist/worker/traversal.worker.d.ts.map +1 -1
  360. package/dist/worker/traversal.worker.js +4 -23
  361. package/dist/worker/traversal.worker.js.map +1 -1
  362. package/package.json +11 -2
@@ -1,43 +1,6 @@
1
1
  /**
2
- * Route drift — static extraction against framework introspection (DEC-115).
3
- *
4
- * The comparison logic only; identity from `@descryy/ir`, never re-implemented
5
- * here. The `bench/route-drift.mjs` instrument does the harder half of this
6
- * row — reading `php artisan route:list`, the `--introspection` file
7
- * fallback, and the refusal to run an installer on a cloned repository (§5
8
- * rule 10, DEC-115 clause 3). This module is the query it calls: given two
9
- * already-extracted route lists, decide what they say about each other.
10
- *
11
- * ## The rule this file is built around, before anything else
12
- *
13
- * **A diff is the one measurement where an empty input produces the most
14
- * reassuring possible output.** If introspection returns nothing —
15
- * `artisan` not runnable, the application not bootable, the command failing
16
- * quietly — the drift is zero, and zero drift is exactly what a healthy
17
- * repository looks like. So both population *sizes* are part of the result,
18
- * not scaffolding, and this module cannot report agreement it was not
19
- * actually given. An empty or implausible side is a **refusal** to report
20
- * drift, never a clean bill — the same shape this lane measured directly on
21
- * `env-precision-adapter.mjs`: two runs both printed `decision accuracy
22
- * 100.0%`, one on 5 decisions and one on 51, indistinguishable except for the
23
- * confidence interval underneath. A diff has no interval to fall back on, so
24
- * the refusal has to be explicit.
25
- *
26
- * ## The polarity is DEC-115's and is not re-opened here
27
- *
28
- * Static extraction is the **floor**; introspection is a **promotion** over
29
- * the same node ids. The two disagreement directions are two findings with
30
- * different audiences and different truth conditions, and collapsing them is
31
- * the error:
32
- *
33
- * | direction | what it is | what it is NOT |
34
- * | --- | --- | --- |
35
- * | static says yes, listing says no | "these two sources disagree" | **never** "dead route" — ambiguous between the customer's app and our own false positive, and nothing here separates them |
36
- * | listing says yes, static says no | a statement about **us**: the extractor missed one | not a finding about the customer at all |
37
- *
38
- * The second row is the more valuable one. Where a listing exists it is
39
- * **ground truth for recall** — a complete denominator this project has
40
- * never had without hand-building it.
2
+ * Route drift (DEC-115): compares two already-extracted route lists; identity via `@descryy/ir`, never re-implemented. An empty/implausible introspection side must be a *refusal*, never reported as zero drift — the emptiest input would otherwise be the most falsely reassuring output.
3
+ * Static extraction is the floor, introspection a promotion (DEC-115 polarity): static-yes/listing-no is an ambiguous disagreement, never "dead route"; listing-yes/static-no is a statement about our own extractor missing a route, and listing is ground truth for recall.
41
4
  */
42
5
  /** One route, from either side, before identity is applied. */
43
6
  export interface DriftRoute {
@@ -46,13 +9,8 @@ export interface DriftRoute {
46
9
  /** Repo-relative, when the side has one (the static extractor always does). */
47
10
  readonly file?: string;
48
11
  }
49
- /**
50
- * What the introspected side actually returned.
51
- *
52
- * Three refusal states plus `read`, mirroring `RollbackReport.ran` /
53
- * `blockers` one level down: a caller needs to know *why* nothing was
54
- * compared, not just that nothing was.
55
- */
12
+ /** What the introspected side returned. Three refusal states plus `read` — a
13
+ * caller needs to know *why* nothing was compared, not just that it wasn't. */
56
14
  export type Introspection = {
57
15
  readonly state: "read";
58
16
  readonly source: string;
@@ -86,23 +44,12 @@ export interface DriftReport {
86
44
  readonly recallAgainstListing?: number;
87
45
  }
88
46
  export interface DriftOptions {
89
- /**
90
- * Below this, a listing is not believed.
91
- *
92
- * A Laravel application with `artisan` and one route is far more likely to
93
- * be a listing that failed than an application with one route. Crude on
94
- * purpose — it exists to catch "the command produced almost nothing," not
95
- * to model route counts.
96
- */
47
+ /** Below this, a listing isn't believed — crude on purpose, catches "the
48
+ * command produced almost nothing," not a route-count model. */
97
49
  readonly implausibleBelow?: number;
98
50
  }
99
- /**
100
- * The comparison, and the refusal that guards it.
101
- *
102
- * `staticOnly`/`listedOnly`/`agreed` are only ever populated when both sides
103
- * were actually read and the introspected side is not implausibly small.
104
- * Every other path returns `reported: false` carrying both counts, because
105
- * the counts are the evidence the refusal was necessary.
106
- */
51
+ /** The comparison, and the refusal that guards it. `staticOnly`/`listedOnly`/
52
+ * `agreed` populate only when both sides read and neither is implausibly
53
+ * small; otherwise `reported: false` carries both counts as evidence. */
107
54
  export declare function checkRouteDrift(staticSide: readonly DriftRoute[], introspected: Introspection, options?: DriftOptions): DriftReport;
108
55
  //# sourceMappingURL=route-drift.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"route-drift.d.ts","sourceRoot":"","sources":["../../src/validation/route-drift.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAIH,+DAA+D;AAC/D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAA;CAAE,GAC3F;IAAE,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,2FAA2F;IAC3F,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;KAAE,EAAE,CAAC;IACtF,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;KAAE,EAAE,CAAC;IACtF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,kFAAkF;IAClF,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,YAAY;IAC3B;;;;;;;OAOG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AASD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,SAAS,UAAU,EAAE,EACjC,YAAY,EAAE,aAAa,EAC3B,OAAO,GAAE,YAAiB,GACzB,WAAW,CAkDb"}
1
+ {"version":3,"file":"route-drift.d.ts","sourceRoot":"","sources":["../../src/validation/route-drift.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,+DAA+D;AAC/D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;gFACgF;AAChF,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAA;CAAE,GAC3F;IAAE,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,2FAA2F;IAC3F,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;KAAE,EAAE,CAAC;IACtF,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;KAAE,EAAE,CAAC;IACtF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,kFAAkF;IAClF,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,YAAY;IAC3B;qEACiE;IACjE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AASD;;0EAE0E;AAC1E,wBAAgB,eAAe,CAC7B,UAAU,EAAE,SAAS,UAAU,EAAE,EACjC,YAAY,EAAE,aAAa,EAC3B,OAAO,GAAE,YAAiB,GACzB,WAAW,CA+Cb"}
@@ -1,43 +1,6 @@
1
1
  /**
2
- * Route drift — static extraction against framework introspection (DEC-115).
3
- *
4
- * The comparison logic only; identity from `@descryy/ir`, never re-implemented
5
- * here. The `bench/route-drift.mjs` instrument does the harder half of this
6
- * row — reading `php artisan route:list`, the `--introspection` file
7
- * fallback, and the refusal to run an installer on a cloned repository (§5
8
- * rule 10, DEC-115 clause 3). This module is the query it calls: given two
9
- * already-extracted route lists, decide what they say about each other.
10
- *
11
- * ## The rule this file is built around, before anything else
12
- *
13
- * **A diff is the one measurement where an empty input produces the most
14
- * reassuring possible output.** If introspection returns nothing —
15
- * `artisan` not runnable, the application not bootable, the command failing
16
- * quietly — the drift is zero, and zero drift is exactly what a healthy
17
- * repository looks like. So both population *sizes* are part of the result,
18
- * not scaffolding, and this module cannot report agreement it was not
19
- * actually given. An empty or implausible side is a **refusal** to report
20
- * drift, never a clean bill — the same shape this lane measured directly on
21
- * `env-precision-adapter.mjs`: two runs both printed `decision accuracy
22
- * 100.0%`, one on 5 decisions and one on 51, indistinguishable except for the
23
- * confidence interval underneath. A diff has no interval to fall back on, so
24
- * the refusal has to be explicit.
25
- *
26
- * ## The polarity is DEC-115's and is not re-opened here
27
- *
28
- * Static extraction is the **floor**; introspection is a **promotion** over
29
- * the same node ids. The two disagreement directions are two findings with
30
- * different audiences and different truth conditions, and collapsing them is
31
- * the error:
32
- *
33
- * | direction | what it is | what it is NOT |
34
- * | --- | --- | --- |
35
- * | static says yes, listing says no | "these two sources disagree" | **never** "dead route" — ambiguous between the customer's app and our own false positive, and nothing here separates them |
36
- * | listing says yes, static says no | a statement about **us**: the extractor missed one | not a finding about the customer at all |
37
- *
38
- * The second row is the more valuable one. Where a listing exists it is
39
- * **ground truth for recall** — a complete denominator this project has
40
- * never had without hand-building it.
2
+ * Route drift (DEC-115): compares two already-extracted route lists; identity via `@descryy/ir`, never re-implemented. An empty/implausible introspection side must be a *refusal*, never reported as zero drift — the emptiest input would otherwise be the most falsely reassuring output.
3
+ * Static extraction is the floor, introspection a promotion (DEC-115 polarity): static-yes/listing-no is an ambiguous disagreement, never "dead route"; listing-yes/static-no is a statement about our own extractor missing a route, and listing is ground truth for recall.
41
4
  */
42
5
  import { endpointQsp, normaliseEndpointPath } from "@descryy/ir";
43
6
  const DEFAULT_IMPLAUSIBLE_BELOW = 3;
@@ -45,14 +8,9 @@ const DEFAULT_IMPLAUSIBLE_BELOW = 3;
45
8
  function key(method, path) {
46
9
  return endpointQsp(String(method || "").toUpperCase(), normaliseEndpointPath(String(path || "")));
47
10
  }
48
- /**
49
- * The comparison, and the refusal that guards it.
50
- *
51
- * `staticOnly`/`listedOnly`/`agreed` are only ever populated when both sides
52
- * were actually read and the introspected side is not implausibly small.
53
- * Every other path returns `reported: false` carrying both counts, because
54
- * the counts are the evidence the refusal was necessary.
55
- */
11
+ /** The comparison, and the refusal that guards it. `staticOnly`/`listedOnly`/
12
+ * `agreed` populate only when both sides read and neither is implausibly
13
+ * small; otherwise `reported: false` carries both counts as evidence. */
56
14
  export function checkRouteDrift(staticSide, introspected, options = {}) {
57
15
  const implausibleBelow = options.implausibleBelow ?? DEFAULT_IMPLAUSIBLE_BELOW;
58
16
  if (introspected.state !== "read") {
@@ -92,11 +50,8 @@ export function checkRouteDrift(staticSide, introspected, options = {}) {
92
50
  staticOnly,
93
51
  listedOnly,
94
52
  agreed,
95
- // `exactOptionalPropertyTypes` distinguishes an absent property from one
96
- // present-and-undefined, so the spread includes the key only when there
97
- // is a real ratio — `listedKeys.size` is never 0 on this path, since
98
- // `implausibleBelow` already refused anything under it, but the type
99
- // stays honest about the precondition rather than asserting past it.
53
+ // listedKeys.size is never 0 here (implausibleBelow refused that), but the
54
+ // type stays honest about the precondition rather than asserting past it.
100
55
  ...(listedKeys.size === 0 ? {} : { recallAgainstListing: agreed / listedKeys.size }),
101
56
  };
102
57
  }
@@ -1 +1 @@
1
- {"version":3,"file":"route-drift.js","sourceRoot":"","sources":["../../src/validation/route-drift.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"route-drift.js","sourceRoot":"","sources":["../../src/validation/route-drift.ts"],"names":[],"mappings":"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"}
@@ -1,47 +1,7 @@
1
- /**
2
- * The declaration side of §15's env/config check — read from the repository, not
3
- * from the graph.
4
- *
5
- * ## Why this is not an adapter, and why it is legal here
6
- *
7
- * §15.3's own wording is *"cross-check against declared variables **or the
8
- * deploy target's actual state**"*. The deploy target's state is not in the graph
9
- * and never will be. What holds it is `.env`, a container manifest, a CI
10
- * workflow, an image build file — **formats, not languages.** Reading
11
- * `KEY=value` and an indented `env:` block names no language, so it does not
12
- * cross the Canonical IR boundary; it is legal above it for the same reason
13
- * reading a commit log is (`sources/git`).
14
- *
15
- * That is what lets the check produce findings with no adapter change at all.
16
- * See DEC-120.
17
- *
18
- * ## Precision over recall, three times, because a misparse here is a wrong finding
19
- *
20
- * A name invented by a sloppy scan becomes a confident sentence about somebody's
21
- * configuration. So:
22
- *
23
- * 1. **Two different name rules, on purpose.** In a dotenv file `KEY=` is
24
- * unambiguous, so any identifier shape is taken. Inside a structured document
25
- * an `env:` block is indistinguishable from any other mapping once the scan
26
- * loses its place, so only `UPPER_SNAKE` is accepted. The cost is a lowercase
27
- * variable in a manifest, which is rare; the alternative is turning a stray
28
- * key into a declaration.
29
- * 2. **A block ends at the first line indented no deeper than its header.** No
30
- * document model, no continuation handling, no anchors. A construct this scan
31
- * cannot follow ends the block early, which *loses* names — a disclosed gap
32
- * rather than a wrong one.
33
- * 3. **A commented-out declaration counts, and is marked.** `# STRIPE_KEY=` in a
34
- * sample file documents that the variable exists, which is exactly the
35
- * question being asked. Treating it as absent would report every well
36
- * documented optional setting as undeclared.
37
- *
38
- * ## The example/supply distinction
39
- *
40
- * `.env.example` **documents**; `.env.development` and a container manifest
41
- * **supply**. Both are declarations for the purpose of "does this repository know
42
- * this variable exists", and they are not interchangeable for anything else, so
43
- * the distinction is carried on the surface rather than flattened.
44
- */
1
+ /** §15's env/config check reads `.env`/manifests/CI directly — formats, not
2
+ * languages, so it never crosses the IR boundary (DEC-120). Precision over
3
+ * recall: strict names, lossy block-end, commented lines count.
4
+ * `.env.example` documents, others supply. */
45
5
  /** What a surface is evidence *of*. */
46
6
  export type SurfaceRole =
47
7
  /** The repository's own statement that a variable exists. */
@@ -71,15 +31,8 @@ export interface SurfaceScan {
71
31
  readonly unreadable: readonly string[];
72
32
  /** True when the walk hit its file budget before finishing. */
73
33
  readonly truncated: boolean;
74
- /**
75
- * The noise directories this walk actually used, echoed back.
76
- *
77
- * Empty means no adapter declared any, and the walk therefore descended into
78
- * whatever vendored trees the repository holds. Reporting it is what keeps an
79
- * undeclared scan from reading like a complete one (rule 7) — the caller can
80
- * see that the surface count is inflated rather than inferring it from a number
81
- * that looks fine.
82
- */
34
+ /** Noise dirs actually used. Empty means none declared (rule 7) — surface
35
+ * count may be inflated by vendored trees. */
83
36
  readonly noiseDirectories: readonly string[];
84
37
  }
85
38
  export interface ScanOptions {
@@ -87,41 +40,21 @@ export interface ScanOptions {
87
40
  readonly maxDepth?: number;
88
41
  /** Total files a walk may consider. */
89
42
  readonly maxFiles?: number;
90
- /**
91
- * Directories this repository's ecosystems install dependencies or build output
92
- * into — `node_modules`, `vendor`, `target`. **Supplied by the caller from the
93
- * active adapters' `CapabilityMatrix.noiseDirectories`, never known here**
94
- * (DEC-110). Undeclared, they are walked, and `SurfaceScan` says so.
95
- */
43
+ /** Ecosystem install/build dirs, supplied by the caller's
44
+ * `CapabilityMatrix.noiseDirectories` (DEC-110); undeclared, they get walked. */
96
45
  readonly noiseDirectories?: readonly string[];
97
46
  }
98
- /**
99
- * Shared with the migration scan, so the two walks cannot disagree about what
100
- * counts as vendored. A second copy of this rule is a second place to forget the
101
- * next ecosystem.
102
- */
47
+ /** Shared with the migration scan — one rule, not two that can disagree. */
103
48
  export declare function skipDirectory(name: string, noiseDirectories?: readonly string[]): boolean;
104
- /**
105
- * Read every configuration surface a repository exposes.
106
- *
107
- * Structured documents are only opened when their path says they are a manifest
108
- * or a workflow. Opening every YAML file in a repository would pull in test
109
- * data, schema definitions and dependency lockfiles, and an `env:` key inside a
110
- * schema definition is a description of a variable rather than a declaration of
111
- * one.
112
- */
49
+ /** Reads every config surface. Structured docs open only when their path
50
+ * names a manifest/workflow, else a schema `env:` key would count as one. */
113
51
  export declare function scanConfigSurfaces(root: string, options?: ScanOptions): SurfaceScan;
114
52
  /** `KEY=value`, and `# KEY=value`, which documents the same variable. */
115
53
  export declare function readDotenv(text: string): readonly EnvDeclaration[];
116
54
  /** `ENV K=V`, the legacy `ENV K V`, and `ARG K`. */
117
55
  export declare function readImageBuild(text: string): readonly EnvDeclaration[];
118
- /**
119
- * `env:` / `environment:` blocks in an indented document, in both forms.
120
- *
121
- * Mapping — `KEY: value` — and sequence — `- KEY=value` or a bare `- KEY`. The
122
- * bare sequence form declares a pass-through from the host, which is still the
123
- * document stating that the variable exists.
124
- */
56
+ /** `env:`/`environment:` blocks: mapping (`KEY: value`) or sequence
57
+ * (`- KEY=value` / bare `- KEY`, a host pass-through — still a declaration). */
125
58
  export declare function readStructured(text: string): readonly EnvDeclaration[];
126
59
  /** One `KEY = "value"` line inside a `wrangler.toml` `[vars]` or `[env.<name>.vars]` table. */
127
60
  export interface WranglerVarsEntry {
@@ -133,20 +66,9 @@ export interface WranglerVarsEntry {
133
66
  readonly line: number;
134
67
  readonly commented: boolean;
135
68
  }
136
- /**
137
- * Walk a `wrangler.toml`'s `[vars]` and `[env.<name>.vars]` tables only.
138
- *
139
- * Every other table — `[[kv_namespaces]]`, `[assets]`, `[env.<name>]` itself
140
- * (the deploy target's own settings, not its vars) — is out of scope by
141
- * construction rather than by exclusion list: entering *any* table this
142
- * function does not recognise as a vars table resets tracking to "outside",
143
- * so a line that merely looks like `KEY = value` inside `[[kv_namespaces]]`
144
- * (e.g. `binding = "HOSTNAMES"`) is never mistaken for a declared variable.
145
- * `[[...]]` array-of-tables headers are matched and reset scope the same way,
146
- * rather than silently falling through as an unrecognised line — an
147
- * `[[env.staging.kv_namespaces]]` line right after `[env.staging.vars]` must
148
- * end that table, not extend it.
149
- */
69
+ /** Only `[vars]`/`[env.<name>.vars]` tables are read; any other table (or
70
+ * `[[...]]` header) resets scope to "outside", so `[[kv_namespaces]]` lines
71
+ * are never mistaken for a var. */
150
72
  export declare function readWranglerVarsEntries(text: string): readonly WranglerVarsEntry[];
151
73
  /** The declaration-only projection `scanConfigSurfaces` uses. See `readWranglerVarsEntries` for the value-carrying form `resolve.ts` reads. */
152
74
  export declare function readWranglerVars(text: string): readonly EnvDeclaration[];
@@ -1 +1 @@
1
- {"version":3,"file":"sources.d.ts","sourceRoot":"","sources":["../../src/validation/sources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAKH,uCAAuC;AACvC,MAAM,MAAM,WAAW;AACrB,6DAA6D;AAC3D,aAAa;AACf,oEAAoE;GAClE,cAAc,CAAC;AAEnB,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,YAAY,GAAG,YAAY,GAAG,cAAc,CAAC;AAEpF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,sFAAsF;IACtF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,SAAS,cAAc,EAAE,CAAC;CAClD;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IAC5C,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B;;;;;;;;OAQG;IACH,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9C;AAED,MAAM,WAAW,WAAW;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,uCAAuC;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/C;AA4DD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,gBAAgB,GAAE,SAAS,MAAM,EAAO,GACvC,OAAO,CAMT;AAgBD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,WAAW,CAmDvF;AAqED,yEAAyE;AACzE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,cAAc,EAAE,CAgBlE;AAED,oDAAoD;AACpD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,cAAc,EAAE,CAwBtE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,cAAc,EAAE,CA4BtE;AAED,+FAA+F;AAC/F,MAAM,WAAW,iBAAiB;IAChC,2HAA2H;IAC3H,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gGAAgG;IAChG,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAWD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,iBAAiB,EAAE,CAoClF;AAED,+IAA+I;AAC/I,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,cAAc,EAAE,CAGxE"}
1
+ {"version":3,"file":"sources.d.ts","sourceRoot":"","sources":["../../src/validation/sources.ts"],"names":[],"mappings":"AAAA;;;+CAG+C;AAK/C,uCAAuC;AACvC,MAAM,MAAM,WAAW;AACrB,6DAA6D;AAC3D,aAAa;AACf,oEAAoE;GAClE,cAAc,CAAC;AAEnB,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,YAAY,GAAG,YAAY,GAAG,cAAc,CAAC;AAEpF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,sFAAsF;IACtF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,SAAS,cAAc,EAAE,CAAC;CAClD;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IAC5C,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B;mDAC+C;IAC/C,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9C;AAED,MAAM,WAAW,WAAW;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,uCAAuC;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;sFACkF;IAClF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/C;AAkBD,4EAA4E;AAC5E,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,gBAAgB,GAAE,SAAS,MAAM,EAAO,GACvC,OAAO,CAKT;AAmCD;8EAC8E;AAC9E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,WAAW,CAsDvF;AA+DD,yEAAyE;AACzE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,cAAc,EAAE,CAelE;AAED,oDAAoD;AACpD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,cAAc,EAAE,CAwBtE;AAED;iFACiF;AACjF,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,cAAc,EAAE,CA4BtE;AAED,+FAA+F;AAC/F,MAAM,WAAW,iBAAiB;IAChC,2HAA2H;IAC3H,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gGAAgG;IAChG,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAWD;;oCAEoC;AACpC,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,iBAAiB,EAAE,CAoClF;AAED,+IAA+I;AAC/I,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,cAAc,EAAE,CAGxE"}
@@ -1,97 +1,15 @@
1
- /**
2
- * The declaration side of §15's env/config check — read from the repository, not
3
- * from the graph.
4
- *
5
- * ## Why this is not an adapter, and why it is legal here
6
- *
7
- * §15.3's own wording is *"cross-check against declared variables **or the
8
- * deploy target's actual state**"*. The deploy target's state is not in the graph
9
- * and never will be. What holds it is `.env`, a container manifest, a CI
10
- * workflow, an image build file — **formats, not languages.** Reading
11
- * `KEY=value` and an indented `env:` block names no language, so it does not
12
- * cross the Canonical IR boundary; it is legal above it for the same reason
13
- * reading a commit log is (`sources/git`).
14
- *
15
- * That is what lets the check produce findings with no adapter change at all.
16
- * See DEC-120.
17
- *
18
- * ## Precision over recall, three times, because a misparse here is a wrong finding
19
- *
20
- * A name invented by a sloppy scan becomes a confident sentence about somebody's
21
- * configuration. So:
22
- *
23
- * 1. **Two different name rules, on purpose.** In a dotenv file `KEY=` is
24
- * unambiguous, so any identifier shape is taken. Inside a structured document
25
- * an `env:` block is indistinguishable from any other mapping once the scan
26
- * loses its place, so only `UPPER_SNAKE` is accepted. The cost is a lowercase
27
- * variable in a manifest, which is rare; the alternative is turning a stray
28
- * key into a declaration.
29
- * 2. **A block ends at the first line indented no deeper than its header.** No
30
- * document model, no continuation handling, no anchors. A construct this scan
31
- * cannot follow ends the block early, which *loses* names — a disclosed gap
32
- * rather than a wrong one.
33
- * 3. **A commented-out declaration counts, and is marked.** `# STRIPE_KEY=` in a
34
- * sample file documents that the variable exists, which is exactly the
35
- * question being asked. Treating it as absent would report every well
36
- * documented optional setting as undeclared.
37
- *
38
- * ## The example/supply distinction
39
- *
40
- * `.env.example` **documents**; `.env.development` and a container manifest
41
- * **supply**. Both are declarations for the purpose of "does this repository know
42
- * this variable exists", and they are not interchangeable for anything else, so
43
- * the distinction is carried on the surface rather than flattened.
44
- */
1
+ /** §15's env/config check reads `.env`/manifests/CI directly — formats, not
2
+ * languages, so it never crosses the IR boundary (DEC-120). Precision over
3
+ * recall: strict names, lossy block-end, commented lines count.
4
+ * `.env.example` documents, others supply. */
45
5
  import { readFileSync, readdirSync, statSync } from "node:fs";
46
6
  import { join, relative, sep } from "node:path";
47
7
  const DEFAULT_MAX_DEPTH = 5;
48
8
  const DEFAULT_MAX_FILES = 20_000;
49
- /**
50
- * Directories a configuration scan must not descend into.
51
- *
52
- * Vendored code carries its own `.env.example` files, and a dependency's sample
53
- * configuration is not this repository's declaration of anything. Including one
54
- * would put another project's variable names into the denominator of the
55
- * coverage guard, which is the number that decides whether anything is reported
56
- * at all.
57
- *
58
- * **The rule is structural, not a list of ecosystems' cache directories.** The
59
- * first version of this was such a list and the boundary lint rejected it, which
60
- * was the correct call: engine code that knows one ecosystem's build cache by
61
- * name is engine code that will be missing the next one's. Every dotted directory
62
- * is skipped apart from the ones that hold pipeline definitions.
63
- *
64
- * **`node_modules`, `vendor`, `target`, `dist`, `build`, `out` and `coverage`
65
- * used to sit here too**, and DEC-110 recorded them as the unfinished half of its
66
- * own ruling — *"three ecosystems' names in the skip list"* — while naming the end
67
- * state: `noiseDirectories` on `CapabilityMatrix`, declared per adapter. They are
68
- * gone from the engine now and arrive through `ScanOptions.noiseDirectories`
69
- * instead. **The knowledge moved to the layer that has it; a second list was not
70
- * created** — DEC-110's *"what must not happen is a third list."*
71
- *
72
- * The cost is real and disclosed rather than hidden: with nothing declared, a
73
- * dependency tree in an undotted directory is walked and a sample inside it is
74
- * read. `SurfaceScan.noiseDirectories` reports the set actually used, so an empty
75
- * declaration cannot pass for a complete one (rule 7), and
76
- * `bench/env-precision.mjs` still reports the surface count per repository.
77
- */
78
- /**
79
- * Dotted directories that are walked anyway, because **configuration lives in
80
- * them**.
81
- *
82
- * The list started as pipelines only and that was too narrow — found by the
83
- * decisions denominator, not by reasoning. `dispatch` keeps a real
84
- * `.devcontainer/.env.example` documenting five variables; skipping it made the
85
- * declaration surface incomplete, which moved the coverage guard *and* made
86
- * three variables look undocumented when the repository documents all three. A
87
- * missing declaration source does not fail loudly here: it silently turns
88
- * correct suppressions into findings.
89
- *
90
- * These are directories a human authors configuration in, as opposed to ones a
91
- * tool writes state into — which is the distinction the dotted-skip rule is
92
- * reaching for and cannot express structurally. DEC-110 records the end state:
93
- * the adapter declares its own noise directories and this list goes away.
94
- */
9
+ /** Skip vendored/build dirs structurally (dotted-dir rule, not an ecosystem
10
+ * list) so another project's `.env.example` can't pollute the denominator. */
11
+ /** Dotted dirs walked anyway — config lives here. Missing one silently turns
12
+ * correct suppressions into findings. Adapter-declared list (DEC-110) replaces this. */
95
13
  const CONFIG_DIRECTORIES = new Set([
96
14
  ".github",
97
15
  ".circleci",
@@ -99,22 +17,36 @@ const CONFIG_DIRECTORIES = new Set([
99
17
  ".devcontainer",
100
18
  ".config",
101
19
  ]);
102
- /**
103
- * Shared with the migration scan, so the two walks cannot disagree about what
104
- * counts as vendored. A second copy of this rule is a second place to forget the
105
- * next ecosystem.
106
- */
20
+ /** Shared with the migration scan — one rule, not two that can disagree. */
107
21
  export function skipDirectory(name, noiseDirectories = []) {
108
- // The structural rule is checked first, and deliberately: a declaration cannot
109
- // re-admit a dotted state directory, and cannot exclude `.github`. The adapter
110
- // declares what its ecosystem installs, not what the engine's own rule means.
22
+ // Structural check first: a declaration can't re-admit a dotted dir or
23
+ // exclude `.github`; adapters declare installs, not the engine's own rule.
111
24
  if (name.startsWith("."))
112
25
  return !CONFIG_DIRECTORIES.has(name);
113
26
  return noiseDirectories.includes(name);
114
27
  }
115
- /** `.env`, `.env.local`, `.env.test` — but not `.env.d.ts` or `.envrc`. */
116
- const DOTENV = /^\.env(\.[A-Za-z0-9_.-]+)?$/;
117
- const EXAMPLE_SUFFIX = /\.(example|sample|template|dist|defaults?)$/i;
28
+ /**
29
+ * `.env`, `.env.local`, `.env-production` — but not `.env.d.ts` or `.envrc`.
30
+ *
31
+ * **The separator is `[.-]`, not `.`, and the leading dot is optional on an
32
+ * example file.** Both were `.`-and-dot-only, and both spellings the pattern
33
+ * missed are in real repositories: `.env-example` and `env.example`. The cost
34
+ * was not a missing surface, it was a **one-sided comparison** — with no
35
+ * example file read, every variable a `.env` sets reads as documented nowhere,
36
+ * so the check either reports the whole file or, past the coverage floor,
37
+ * reports nothing and says the examples cover 0%.
38
+ *
39
+ * The dotless form is admitted **only when it carries an example suffix**. A
40
+ * bare `env` is as likely a shell script or a directory as a declaration, and
41
+ * `env.ts` is code; requiring the suffix keeps both out rather than relying on
42
+ * the reader finding no `KEY=` in them.
43
+ */
44
+ const DOTENV = /^\.env([.-][A-Za-z0-9_.-]+)?$/;
45
+ /** `env.example`, `env-sample` — the same file, without the leading dot. */
46
+ const BARE_ENV_EXAMPLE = /^env[.-](example|sample|template|dist|defaults?)$/i;
47
+ /** TypeScript that happens to be named for the environment. Never a declaration. */
48
+ const DECLARATION_FILE = /\.d\.[cm]?ts$/i;
49
+ const EXAMPLE_SUFFIX = /[.-](example|sample|template|dist|defaults?)$/i;
118
50
  const CONTAINER_MANIFEST = /^(docker-)?compose([.-][A-Za-z0-9_.-]+)?\.ya?ml$/i;
119
51
  const IMAGE_BUILD = /^(Containerfile|Dockerfile)([.-][A-Za-z0-9_.-]+)?$/i;
120
52
  const STRUCTURED = /\.ya?ml$/i;
@@ -124,15 +56,8 @@ const WRANGLER_TOML = /^wrangler\.toml$/;
124
56
  const UPPER_SNAKE = /^[A-Z][A-Z0-9_]*$/;
125
57
  /** Any identifier shape, for the unambiguous `KEY=` form. */
126
58
  const ANY_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
127
- /**
128
- * Read every configuration surface a repository exposes.
129
- *
130
- * Structured documents are only opened when their path says they are a manifest
131
- * or a workflow. Opening every YAML file in a repository would pull in test
132
- * data, schema definitions and dependency lockfiles, and an `env:` key inside a
133
- * schema definition is a description of a variable rather than a declaration of
134
- * one.
135
- */
59
+ /** Reads every config surface. Structured docs open only when their path
60
+ * names a manifest/workflow, else a schema `env:` key would count as one. */
136
61
  export function scanConfigSurfaces(root, options = {}) {
137
62
  const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
138
63
  const maxFiles = options.maxFiles ?? DEFAULT_MAX_FILES;
@@ -161,6 +86,9 @@ export function scanConfigSurfaces(root, options = {}) {
161
86
  directory = statSync(full).isDirectory();
162
87
  }
163
88
  catch {
89
+ // Same as an unreadable dir, on one entry (broken symlink, perm error,
90
+ // race). Disclosed via `unreadable` (rule 7), not dropped silently.
91
+ unreadable.push(rel(root, full));
164
92
  continue;
165
93
  }
166
94
  if (directory) {
@@ -188,17 +116,16 @@ export function scanConfigSurfaces(root, options = {}) {
188
116
  return { surfaces, unreadable, truncated, noiseDirectories };
189
117
  }
190
118
  function classify(root, dir, entry) {
191
- if (DOTENV.test(entry)) {
192
- const example = EXAMPLE_SUFFIX.test(entry) || entry === ".env.example";
119
+ if ((DOTENV.test(entry) || BARE_ENV_EXAMPLE.test(entry)) && !DECLARATION_FILE.test(entry)) {
120
+ const example = EXAMPLE_SUFFIX.test(entry);
193
121
  // An example file states what exists; a concrete one states what is set.
194
122
  // Neither is on a path to production by itself, so both are declarations —
195
123
  // the deploy target is the manifest that mounts them.
196
124
  return { format: "dotenv", role: "declaration", example };
197
125
  }
198
126
  if (WRANGLER_TOML.test(entry)) {
199
- // Same status `.env.development` holds: the repository's own statement of
200
- // what it runs with, not a deploy pipeline's manifest of it. `[vars]` and
201
- // `.env.development` are read the same way for the same reason.
127
+ // Same status as `.env.development`: repo's own statement, not a deploy
128
+ // manifest — `[vars]` read the same way for the same reason.
202
129
  return { format: "wranglerVars", role: "declaration", example: false };
203
130
  }
204
131
  if (IMAGE_BUILD.test(entry)) {
@@ -212,13 +139,8 @@ function classify(root, dir, entry) {
212
139
  }
213
140
  return null;
214
141
  }
215
- /**
216
- * Only structured documents whose *path* declares them a pipeline are opened.
217
- *
218
- * Named rather than pattern-matched on content: a document that happens to
219
- * contain an `env:` key is not necessarily setting one, and guessing from
220
- * content is how a schema fragment becomes a declaration.
221
- */
142
+ /** Only path-classified pipeline docs are opened — matching content (an
143
+ * `env:` key) would turn a schema fragment into a false declaration. */
222
144
  function isPipelineDirectory(dir) {
223
145
  const parts = dir.split("/");
224
146
  return (parts.includes("workflows") ||
@@ -250,8 +172,7 @@ export function readDotenv(text) {
250
172
  for (let i = 0; i < lines.length; i += 1) {
251
173
  const raw = lines[i] ?? "";
252
174
  const commented = /^\s*#/.test(raw);
253
- // `export KEY=value` is the shell-sourceable form of the same file and is
254
- // common in deployment scripts.
175
+ // `export KEY=value` — shell-sourceable form, common in deploy scripts.
255
176
  const match = /^\s*(?:#\s*)?(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/.exec(raw);
256
177
  if (match === null)
257
178
  continue;
@@ -290,13 +211,8 @@ export function readImageBuild(text) {
290
211
  }
291
212
  return dedupe(out);
292
213
  }
293
- /**
294
- * `env:` / `environment:` blocks in an indented document, in both forms.
295
- *
296
- * Mapping — `KEY: value` — and sequence — `- KEY=value` or a bare `- KEY`. The
297
- * bare sequence form declares a pass-through from the host, which is still the
298
- * document stating that the variable exists.
299
- */
214
+ /** `env:`/`environment:` blocks: mapping (`KEY: value`) or sequence
215
+ * (`- KEY=value` / bare `- KEY`, a host pass-through — still a declaration). */
300
216
  export function readStructured(text) {
301
217
  const out = [];
302
218
  const lines = text.split(/\r?\n/);
@@ -334,20 +250,9 @@ function wranglerTableEnvironment(table) {
334
250
  return "default";
335
251
  return WRANGLER_ENV_VARS_TABLE.exec(trimmed)?.[1] ?? null;
336
252
  }
337
- /**
338
- * Walk a `wrangler.toml`'s `[vars]` and `[env.<name>.vars]` tables only.
339
- *
340
- * Every other table — `[[kv_namespaces]]`, `[assets]`, `[env.<name>]` itself
341
- * (the deploy target's own settings, not its vars) — is out of scope by
342
- * construction rather than by exclusion list: entering *any* table this
343
- * function does not recognise as a vars table resets tracking to "outside",
344
- * so a line that merely looks like `KEY = value` inside `[[kv_namespaces]]`
345
- * (e.g. `binding = "HOSTNAMES"`) is never mistaken for a declared variable.
346
- * `[[...]]` array-of-tables headers are matched and reset scope the same way,
347
- * rather than silently falling through as an unrecognised line — an
348
- * `[[env.staging.kv_namespaces]]` line right after `[env.staging.vars]` must
349
- * end that table, not extend it.
350
- */
253
+ /** Only `[vars]`/`[env.<name>.vars]` tables are read; any other table (or
254
+ * `[[...]]` header) resets scope to "outside", so `[[kv_namespaces]]` lines
255
+ * are never mistaken for a var. */
351
256
  export function readWranglerVarsEntries(text) {
352
257
  const out = [];
353
258
  const lines = text.split(/\r?\n/);
@@ -394,8 +299,7 @@ function dedupe(declarations) {
394
299
  const seen = new Map();
395
300
  for (const declaration of declarations) {
396
301
  const existing = seen.get(declaration.name);
397
- // An active assignment outranks a commented one even if it comes later:
398
- // "set here" is a stronger fact than "documented here".
302
+ // Active assignment outranks a later commented one: "set" beats "documented".
399
303
  if (existing === undefined || (existing.commented && !declaration.commented)) {
400
304
  seen.set(declaration.name, declaration);
401
305
  }