@mailwoman/corpus 8.6.0 → 9.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 (497) hide show
  1. package/README.md +2 -2
  2. package/data/PROVENANCE.md +214 -0
  3. package/data/sub-venue-lexicon.json +18269 -0
  4. package/out/src/adapters/ban/adapter.d.ts +5 -4
  5. package/out/src/adapters/ban/adapter.d.ts.map +1 -1
  6. package/out/src/adapters/ban/adapter.js +62 -69
  7. package/out/src/adapters/ban/adapter.js.map +1 -1
  8. package/out/src/adapters/ban/street-decompose.d.ts.map +1 -1
  9. package/out/src/adapters/ban/street-decompose.js +18 -25
  10. package/out/src/adapters/ban/street-decompose.js.map +1 -1
  11. package/out/src/adapters/fcc-bdc/adapter.d.ts +1 -7
  12. package/out/src/adapters/fcc-bdc/adapter.d.ts.map +1 -1
  13. package/out/src/adapters/fcc-bdc/adapter.js +4 -26
  14. package/out/src/adapters/fcc-bdc/adapter.js.map +1 -1
  15. package/out/src/adapters/geonames/adapter.d.ts +1 -1
  16. package/out/src/adapters/geonames/adapter.d.ts.map +1 -1
  17. package/out/src/adapters/geonames/adapter.js +72 -78
  18. package/out/src/adapters/geonames/adapter.js.map +1 -1
  19. package/out/src/adapters/geonames-postal/adapter.d.ts +1 -1
  20. package/out/src/adapters/geonames-postal/adapter.d.ts.map +1 -1
  21. package/out/src/adapters/geonames-postal/adapter.js +46 -51
  22. package/out/src/adapters/geonames-postal/adapter.js.map +1 -1
  23. package/out/src/adapters/gnaf/adapter.d.ts +1 -1
  24. package/out/src/adapters/gnaf/adapter.d.ts.map +1 -1
  25. package/out/src/adapters/gnaf/adapter.js +6 -9
  26. package/out/src/adapters/gnaf/adapter.js.map +1 -1
  27. package/out/src/adapters/gnaf/assemble.d.ts.map +1 -1
  28. package/out/src/adapters/gnaf/assemble.js +7 -12
  29. package/out/src/adapters/gnaf/assemble.js.map +1 -1
  30. package/out/src/adapters/index.d.ts +1 -1
  31. package/out/src/adapters/index.d.ts.map +1 -1
  32. package/out/src/adapters/index.js +1 -1
  33. package/out/src/adapters/index.js.map +1 -1
  34. package/out/src/adapters/openaddresses/adapter.d.ts +1 -1
  35. package/out/src/adapters/openaddresses/adapter.d.ts.map +1 -1
  36. package/out/src/adapters/openaddresses/adapter.js +5 -10
  37. package/out/src/adapters/openaddresses/adapter.js.map +1 -1
  38. package/out/src/adapters/overture/adapter.d.ts +1 -1
  39. package/out/src/adapters/overture/adapter.d.ts.map +1 -1
  40. package/out/src/adapters/overture/adapter.js +5 -9
  41. package/out/src/adapters/overture/adapter.js.map +1 -1
  42. package/out/src/adapters/state-hi-schools/adapter.d.ts +1 -1
  43. package/out/src/adapters/state-hi-schools/adapter.d.ts.map +1 -1
  44. package/out/src/adapters/state-hi-schools/adapter.js +57 -75
  45. package/out/src/adapters/state-hi-schools/adapter.js.map +1 -1
  46. package/out/src/adapters/state-ia-contractors/adapter.d.ts +1 -1
  47. package/out/src/adapters/state-ia-contractors/adapter.d.ts.map +1 -1
  48. package/out/src/adapters/state-ia-contractors/adapter.js +60 -78
  49. package/out/src/adapters/state-ia-contractors/adapter.js.map +1 -1
  50. package/out/src/adapters/state-ny-notaries/adapter.d.ts +1 -1
  51. package/out/src/adapters/state-ny-notaries/adapter.d.ts.map +1 -1
  52. package/out/src/adapters/state-ny-notaries/adapter.js +69 -87
  53. package/out/src/adapters/state-ny-notaries/adapter.js.map +1 -1
  54. package/out/src/adapters/state-tx-notaries/adapter.d.ts +1 -1
  55. package/out/src/adapters/state-tx-notaries/adapter.d.ts.map +1 -1
  56. package/out/src/adapters/state-tx-notaries/adapter.js +71 -89
  57. package/out/src/adapters/state-tx-notaries/adapter.js.map +1 -1
  58. package/out/src/adapters/synth-po-box/adapter.d.ts +3 -3
  59. package/out/src/adapters/synth-po-box/adapter.d.ts.map +1 -1
  60. package/out/src/adapters/synth-po-box/adapter.js +10 -18
  61. package/out/src/adapters/synth-po-box/adapter.js.map +1 -1
  62. package/out/src/adapters/tiger/adapter.d.ts +1 -1
  63. package/out/src/adapters/tiger/adapter.d.ts.map +1 -1
  64. package/out/src/adapters/tiger/adapter.js +2 -2
  65. package/out/src/adapters/tiger/adapter.js.map +1 -1
  66. package/out/src/adapters/tiger/street-decompose.d.ts.map +1 -1
  67. package/out/src/adapters/tiger/street-decompose.js +18 -27
  68. package/out/src/adapters/tiger/street-decompose.js.map +1 -1
  69. package/out/src/adapters/usgov-hrsa-fqhc/adapter.d.ts +4 -4
  70. package/out/src/adapters/usgov-hrsa-fqhc/adapter.d.ts.map +1 -1
  71. package/out/src/adapters/usgov-hrsa-fqhc/adapter.js +64 -82
  72. package/out/src/adapters/usgov-hrsa-fqhc/adapter.js.map +1 -1
  73. package/out/src/adapters/usgov-imls-pls/adapter.d.ts +1 -1
  74. package/out/src/adapters/usgov-imls-pls/adapter.d.ts.map +1 -1
  75. package/out/src/adapters/usgov-imls-pls/adapter.js +62 -84
  76. package/out/src/adapters/usgov-imls-pls/adapter.js.map +1 -1
  77. package/out/src/adapters/usgov-irs-bmf/adapter.d.ts +1 -1
  78. package/out/src/adapters/usgov-irs-bmf/adapter.d.ts.map +1 -1
  79. package/out/src/adapters/usgov-irs-bmf/adapter.js +59 -66
  80. package/out/src/adapters/usgov-irs-bmf/adapter.js.map +1 -1
  81. package/out/src/adapters/usgov-nad/adapter.d.ts +1 -1
  82. package/out/src/adapters/usgov-nad/adapter.d.ts.map +1 -1
  83. package/out/src/adapters/usgov-nad/adapter.js +6 -9
  84. package/out/src/adapters/usgov-nad/adapter.js.map +1 -1
  85. package/out/src/adapters/usgov-nppes/adapter.d.ts +1 -1
  86. package/out/src/adapters/usgov-nppes/adapter.d.ts.map +1 -1
  87. package/out/src/adapters/usgov-nppes/adapter.js +60 -78
  88. package/out/src/adapters/usgov-nppes/adapter.js.map +1 -1
  89. package/out/src/adapters/usgov-samhsa-treatment-locator/adapter.d.ts +1 -1
  90. package/out/src/adapters/usgov-samhsa-treatment-locator/adapter.d.ts.map +1 -1
  91. package/out/src/adapters/usgov-samhsa-treatment-locator/adapter.js +53 -73
  92. package/out/src/adapters/usgov-samhsa-treatment-locator/adapter.js.map +1 -1
  93. package/out/src/{adapter.d.ts → adapters/utils/index.d.ts} +43 -2
  94. package/out/src/adapters/utils/index.d.ts.map +1 -0
  95. package/out/src/{adapter.js → adapters/utils/index.js} +47 -2
  96. package/out/src/adapters/utils/index.js.map +1 -0
  97. package/out/src/adapters/wof-admin-jp/adapter.d.ts +1 -1
  98. package/out/src/adapters/wof-admin-jp/adapter.d.ts.map +1 -1
  99. package/out/src/adapters/wof-admin-jp/adapter.js +24 -6
  100. package/out/src/adapters/wof-admin-jp/adapter.js.map +1 -1
  101. package/out/src/adapters/wof-admin-json/adapter.d.ts +2 -2
  102. package/out/src/adapters/wof-admin-json/adapter.d.ts.map +1 -1
  103. package/out/src/adapters/wof-admin-json/adapter.js +2 -2
  104. package/out/src/adapters/wof-admin-json/adapter.js.map +1 -1
  105. package/out/src/adapters/wof-postalcode-json/adapter.d.ts +2 -2
  106. package/out/src/adapters/wof-postalcode-json/adapter.d.ts.map +1 -1
  107. package/out/src/adapters/wof-postalcode-json/adapter.js +2 -2
  108. package/out/src/adapters/wof-postalcode-json/adapter.js.map +1 -1
  109. package/out/src/build.d.ts +3 -4
  110. package/out/src/build.d.ts.map +1 -1
  111. package/out/src/build.js +6 -8
  112. package/out/src/build.js.map +1 -1
  113. package/out/src/index.d.ts +3 -17
  114. package/out/src/index.d.ts.map +1 -1
  115. package/out/src/index.js +3 -17
  116. package/out/src/index.js.map +1 -1
  117. package/out/src/name-prone-us-suffixes.d.ts +12 -0
  118. package/out/src/name-prone-us-suffixes.d.ts.map +1 -0
  119. package/out/src/name-prone-us-suffixes.js +12 -0
  120. package/out/src/name-prone-us-suffixes.js.map +1 -0
  121. package/out/src/parquet-wrapper/reader.d.ts +8 -0
  122. package/out/src/parquet-wrapper/reader.d.ts.map +1 -1
  123. package/out/src/parquet-wrapper/reader.js +16 -0
  124. package/out/src/parquet-wrapper/reader.js.map +1 -1
  125. package/out/src/parquet-wrapper/schema.d.ts +4 -0
  126. package/out/src/parquet-wrapper/schema.d.ts.map +1 -1
  127. package/out/src/parquet-wrapper/schema.js.map +1 -1
  128. package/out/src/runner.d.ts +2 -2
  129. package/out/src/runner.d.ts.map +1 -1
  130. package/out/src/runner.js +1 -1
  131. package/out/src/runner.js.map +1 -1
  132. package/out/src/shard-recipes/anchor-absorption.d.ts.map +1 -1
  133. package/out/src/shard-recipes/anchor-absorption.js +5 -4
  134. package/out/src/shard-recipes/anchor-absorption.js.map +1 -1
  135. package/out/src/shard-recipes/boundary-stress.d.ts.map +1 -1
  136. package/out/src/shard-recipes/boundary-stress.js +2 -2
  137. package/out/src/shard-recipes/boundary-stress.js.map +1 -1
  138. package/out/src/shard-recipes/country-balanced.d.ts.map +1 -1
  139. package/out/src/shard-recipes/country-balanced.js +75 -75
  140. package/out/src/shard-recipes/country-balanced.js.map +1 -1
  141. package/out/src/shard-recipes/fr-admin-split.js +2 -2
  142. package/out/src/shard-recipes/fr-admin-split.js.map +1 -1
  143. package/out/src/shard-recipes/fr-fragment.d.ts.map +1 -1
  144. package/out/src/shard-recipes/fr-fragment.js +8 -5
  145. package/out/src/shard-recipes/fr-fragment.js.map +1 -1
  146. package/out/src/shard-recipes/fr-lieudit.js +2 -2
  147. package/out/src/shard-recipes/fr-lieudit.js.map +1 -1
  148. package/out/src/shard-recipes/fr-order.d.ts.map +1 -1
  149. package/out/src/shard-recipes/fr-order.js +18 -72
  150. package/out/src/shard-recipes/fr-order.js.map +1 -1
  151. package/out/src/shard-recipes/german.d.ts.map +1 -1
  152. package/out/src/shard-recipes/german.js +17 -70
  153. package/out/src/shard-recipes/german.js.map +1 -1
  154. package/out/src/shard-recipes/house-venue.d.ts.map +1 -1
  155. package/out/src/shard-recipes/house-venue.js +1 -1
  156. package/out/src/shard-recipes/house-venue.js.map +1 -1
  157. package/out/src/shard-recipes/index.d.ts.map +1 -1
  158. package/out/src/shard-recipes/index.js +4 -1
  159. package/out/src/shard-recipes/index.js.map +1 -1
  160. package/out/src/shard-recipes/intersection.d.ts +2 -2
  161. package/out/src/shard-recipes/intersection.d.ts.map +1 -1
  162. package/out/src/shard-recipes/intersection.js +28 -72
  163. package/out/src/shard-recipes/intersection.js.map +1 -1
  164. package/out/src/shard-recipes/locale.d.ts +5 -5
  165. package/out/src/shard-recipes/locale.d.ts.map +1 -1
  166. package/out/src/shard-recipes/locale.js +25 -35
  167. package/out/src/shard-recipes/locale.js.map +1 -1
  168. package/out/src/shard-recipes/no-fragment.d.ts.map +1 -1
  169. package/out/src/shard-recipes/no-fragment.js +8 -5
  170. package/out/src/shard-recipes/no-fragment.js.map +1 -1
  171. package/out/src/shard-recipes/no-street-led.d.ts.map +1 -1
  172. package/out/src/shard-recipes/no-street-led.js +8 -5
  173. package/out/src/shard-recipes/no-street-led.js.map +1 -1
  174. package/out/src/shard-recipes/no-street.d.ts.map +1 -1
  175. package/out/src/shard-recipes/no-street.js +1 -1
  176. package/out/src/shard-recipes/no-street.js.map +1 -1
  177. package/out/src/shard-recipes/po-box-cedex.d.ts +3 -3
  178. package/out/src/shard-recipes/po-box-cedex.d.ts.map +1 -1
  179. package/out/src/shard-recipes/po-box-cedex.js +101 -127
  180. package/out/src/shard-recipes/po-box-cedex.js.map +1 -1
  181. package/out/src/shard-recipes/po-box.d.ts.map +1 -1
  182. package/out/src/shard-recipes/po-box.js +1 -1
  183. package/out/src/shard-recipes/po-box.js.map +1 -1
  184. package/out/src/shard-recipes/scaffold.d.ts +58 -9
  185. package/out/src/shard-recipes/scaffold.d.ts.map +1 -1
  186. package/out/src/shard-recipes/scaffold.js +87 -29
  187. package/out/src/shard-recipes/scaffold.js.map +1 -1
  188. package/out/src/shard-recipes/street-affix.d.ts +54 -0
  189. package/out/src/shard-recipes/street-affix.d.ts.map +1 -1
  190. package/out/src/shard-recipes/street-affix.js +253 -111
  191. package/out/src/shard-recipes/street-affix.js.map +1 -1
  192. package/out/src/shard-recipes/street-bare.d.ts.map +1 -1
  193. package/out/src/shard-recipes/street-bare.js +3 -3
  194. package/out/src/shard-recipes/street-bare.js.map +1 -1
  195. package/out/src/shard-recipes/street.d.ts.map +1 -1
  196. package/out/src/shard-recipes/street.js +2 -2
  197. package/out/src/shard-recipes/street.js.map +1 -1
  198. package/out/src/shard-recipes/sub-venue-sources.d.ts +228 -0
  199. package/out/src/shard-recipes/sub-venue-sources.d.ts.map +1 -0
  200. package/out/src/shard-recipes/sub-venue-sources.js +664 -0
  201. package/out/src/shard-recipes/sub-venue-sources.js.map +1 -0
  202. package/out/src/shard-recipes/sub-venue.d.ts +269 -0
  203. package/out/src/shard-recipes/sub-venue.d.ts.map +1 -0
  204. package/out/src/shard-recipes/sub-venue.js +709 -0
  205. package/out/src/shard-recipes/sub-venue.js.map +1 -0
  206. package/out/src/shard-recipes/unit.d.ts +1 -1
  207. package/out/src/shard-recipes/unit.d.ts.map +1 -1
  208. package/out/src/shard-recipes/unit.js +26 -74
  209. package/out/src/shard-recipes/unit.js.map +1 -1
  210. package/out/src/{synthesize-anchor-absorption.d.ts → synthesizers/anchor-absorption.d.ts} +1 -1
  211. package/out/src/synthesizers/anchor-absorption.d.ts.map +1 -0
  212. package/out/src/{synthesize-anchor-absorption.js → synthesizers/anchor-absorption.js} +1 -1
  213. package/out/src/synthesizers/anchor-absorption.js.map +1 -0
  214. package/out/src/{synthesize-boundary-stress.d.ts → synthesizers/boundary-stress.d.ts} +3 -3
  215. package/out/src/synthesizers/boundary-stress.d.ts.map +1 -0
  216. package/out/src/{synthesize-boundary-stress.js → synthesizers/boundary-stress.js} +2 -2
  217. package/out/src/synthesizers/boundary-stress.js.map +1 -0
  218. package/out/src/{synthesize-german.d.ts → synthesizers/german.d.ts} +2 -2
  219. package/out/src/synthesizers/german.d.ts.map +1 -0
  220. package/out/src/{synthesize-german.js → synthesizers/german.js} +2 -2
  221. package/out/src/synthesizers/german.js.map +1 -0
  222. package/out/src/{synthesize-house-venue.d.ts → synthesizers/house-venue.d.ts} +4 -4
  223. package/out/src/synthesizers/house-venue.d.ts.map +1 -0
  224. package/out/src/{synthesize-house-venue.js → synthesizers/house-venue.js} +4 -4
  225. package/out/src/synthesizers/house-venue.js.map +1 -0
  226. package/out/src/{synthesize-intersection.d.ts → synthesizers/intersection.d.ts} +2 -2
  227. package/out/src/synthesizers/intersection.d.ts.map +1 -0
  228. package/out/src/{synthesize-intersection.js → synthesizers/intersection.js} +1 -1
  229. package/out/src/synthesizers/intersection.js.map +1 -0
  230. package/out/src/{synthesize-no-street.d.ts → synthesizers/no-street.d.ts} +3 -3
  231. package/out/src/synthesizers/no-street.d.ts.map +1 -0
  232. package/out/src/{synthesize-no-street.js → synthesizers/no-street.js} +2 -2
  233. package/out/src/synthesizers/no-street.js.map +1 -0
  234. package/out/src/{synthesize-po-box.d.ts → synthesizers/po-box.d.ts} +2 -2
  235. package/out/src/synthesizers/po-box.d.ts.map +1 -0
  236. package/out/src/{synthesize-po-box.js → synthesizers/po-box.js} +1 -1
  237. package/out/src/synthesizers/po-box.js.map +1 -0
  238. package/out/src/{synthesize-street.d.ts → synthesizers/street.d.ts} +2 -2
  239. package/out/src/synthesizers/street.d.ts.map +1 -0
  240. package/out/src/{synthesize-street.js → synthesizers/street.js} +2 -2
  241. package/out/src/synthesizers/street.js.map +1 -0
  242. package/out/src/{synthesize.d.ts → synthesizers/utils.d.ts} +3 -3
  243. package/out/src/synthesizers/utils.d.ts.map +1 -0
  244. package/out/src/{synthesize.js → synthesizers/utils.js} +4 -15
  245. package/out/src/synthesizers/utils.js.map +1 -0
  246. package/out/src/tools/align-shard.d.ts.map +1 -1
  247. package/out/src/tools/align-shard.js +4 -8
  248. package/out/src/tools/align-shard.js.map +1 -1
  249. package/out/src/tools/audit.d.ts.map +1 -1
  250. package/out/src/tools/audit.js +4 -4
  251. package/out/src/tools/audit.js.map +1 -1
  252. package/out/src/tools/corpus-stats.d.ts +2 -2
  253. package/out/src/tools/corpus-stats.d.ts.map +1 -1
  254. package/out/src/tools/corpus-stats.js +18 -31
  255. package/out/src/tools/corpus-stats.js.map +1 -1
  256. package/out/src/tools/fetch/download.d.ts.map +1 -1
  257. package/out/src/tools/fetch/download.js +5 -6
  258. package/out/src/tools/fetch/download.js.map +1 -1
  259. package/out/src/tools/fetch/imls-pls.d.ts.map +1 -1
  260. package/out/src/tools/fetch/imls-pls.js +3 -15
  261. package/out/src/tools/fetch/imls-pls.js.map +1 -1
  262. package/out/src/tools/fetch/index.d.ts +14 -1
  263. package/out/src/tools/fetch/index.d.ts.map +1 -1
  264. package/out/src/tools/fetch/index.js +14 -1
  265. package/out/src/tools/fetch/index.js.map +1 -1
  266. package/out/src/tools/fetch/nad.d.ts +1 -1
  267. package/out/src/tools/fetch/nad.js +1 -1
  268. package/out/src/tools/fetch/nppes.d.ts.map +1 -1
  269. package/out/src/tools/fetch/nppes.js +8 -14
  270. package/out/src/tools/fetch/nppes.js.map +1 -1
  271. package/out/src/tools/fetch/openaddresses.d.ts +1 -1
  272. package/out/src/tools/fetch/openaddresses.js +1 -1
  273. package/out/src/tools/fetch/ourairports.d.ts +45 -0
  274. package/out/src/tools/fetch/ourairports.d.ts.map +1 -0
  275. package/out/src/tools/fetch/ourairports.js +123 -0
  276. package/out/src/tools/fetch/ourairports.js.map +1 -0
  277. package/out/src/tools/fetch/wikidata-subvenue.d.ts +171 -0
  278. package/out/src/tools/fetch/wikidata-subvenue.d.ts.map +1 -0
  279. package/out/src/tools/fetch/wikidata-subvenue.js +275 -0
  280. package/out/src/tools/fetch/wikidata-subvenue.js.map +1 -0
  281. package/out/src/tools/golden-expand.d.ts.map +1 -1
  282. package/out/src/tools/golden-expand.js +18 -16
  283. package/out/src/tools/golden-expand.js.map +1 -1
  284. package/out/src/tools/golden-promote.d.ts.map +1 -1
  285. package/out/src/tools/golden-promote.js +12 -6
  286. package/out/src/tools/golden-promote.js.map +1 -1
  287. package/out/src/tools/golden-relabel-street.d.ts +196 -0
  288. package/out/src/tools/golden-relabel-street.d.ts.map +1 -0
  289. package/out/src/tools/golden-relabel-street.js +440 -0
  290. package/out/src/tools/golden-relabel-street.js.map +1 -0
  291. package/out/src/tools/index.d.ts +4 -0
  292. package/out/src/tools/index.d.ts.map +1 -1
  293. package/out/src/tools/index.js +4 -0
  294. package/out/src/tools/index.js.map +1 -1
  295. package/out/src/tools/jsonl-to-parquet.d.ts.map +1 -1
  296. package/out/src/tools/jsonl-to-parquet.js +3 -2
  297. package/out/src/tools/jsonl-to-parquet.js.map +1 -1
  298. package/out/src/tools/lint-shard-vocab.d.ts.map +1 -1
  299. package/out/src/tools/lint-shard-vocab.js +3 -2
  300. package/out/src/tools/lint-shard-vocab.js.map +1 -1
  301. package/out/src/tools/lint-shard.d.ts +3 -3
  302. package/out/src/tools/lint-shard.d.ts.map +1 -1
  303. package/out/src/tools/lint-shard.js +23 -32
  304. package/out/src/tools/lint-shard.js.map +1 -1
  305. package/out/src/tools/overlay-manifest.d.ts.map +1 -1
  306. package/out/src/tools/overlay-manifest.js +2 -1
  307. package/out/src/tools/overlay-manifest.js.map +1 -1
  308. package/out/src/tools/overture-subvenue.d.ts +112 -0
  309. package/out/src/tools/overture-subvenue.d.ts.map +1 -0
  310. package/out/src/tools/overture-subvenue.js +144 -0
  311. package/out/src/tools/overture-subvenue.js.map +1 -0
  312. package/out/src/tools/shard-kryptonite.d.ts +1 -1
  313. package/out/src/tools/shard-kryptonite.d.ts.map +1 -1
  314. package/out/src/tools/shard-kryptonite.js +6 -6
  315. package/out/src/tools/shard-kryptonite.js.map +1 -1
  316. package/out/src/tools/shard-translit.d.ts +6 -3
  317. package/out/src/tools/shard-translit.d.ts.map +1 -1
  318. package/out/src/tools/shard-translit.js +9 -8
  319. package/out/src/tools/shard-translit.js.map +1 -1
  320. package/out/src/tools/sub-venue-lexicon.d.ts +507 -0
  321. package/out/src/tools/sub-venue-lexicon.d.ts.map +1 -0
  322. package/out/src/tools/sub-venue-lexicon.js +817 -0
  323. package/out/src/tools/sub-venue-lexicon.js.map +1 -0
  324. package/out/src/tools/sub-venue-promotions.d.ts +94 -0
  325. package/out/src/tools/sub-venue-promotions.d.ts.map +1 -0
  326. package/out/src/tools/sub-venue-promotions.js +266 -0
  327. package/out/src/tools/sub-venue-promotions.js.map +1 -0
  328. package/out/src/{align.d.ts → utils/align.d.ts} +1 -1
  329. package/out/src/utils/align.d.ts.map +1 -0
  330. package/out/src/utils/align.js.map +1 -0
  331. package/out/src/{golden.d.ts → utils/golden.d.ts} +5 -0
  332. package/out/src/utils/golden.d.ts.map +1 -0
  333. package/out/src/{golden.js → utils/golden.js} +24 -10
  334. package/out/src/utils/golden.js.map +1 -0
  335. package/out/src/utils/index.d.ts +14 -0
  336. package/out/src/utils/index.d.ts.map +1 -0
  337. package/out/src/utils/index.js +14 -0
  338. package/out/src/utils/index.js.map +1 -0
  339. package/out/src/utils/license.d.ts.map +1 -0
  340. package/out/src/utils/license.js.map +1 -0
  341. package/out/src/{parquet.d.ts → utils/parquet.d.ts} +2 -2
  342. package/out/src/utils/parquet.d.ts.map +1 -0
  343. package/out/src/{parquet.js → utils/parquet.js} +3 -15
  344. package/out/src/utils/parquet.js.map +1 -0
  345. package/out/src/{split.d.ts → utils/split.d.ts} +8 -1
  346. package/out/src/utils/split.d.ts.map +1 -0
  347. package/out/src/{split.js → utils/split.js} +7 -0
  348. package/out/src/utils/split.js.map +1 -0
  349. package/out/src/utils/tokenize.d.ts.map +1 -0
  350. package/out/src/utils/tokenize.js.map +1 -0
  351. package/out/src/{wof-json.d.ts → utils/wof-json.d.ts} +2 -2
  352. package/out/src/utils/wof-json.d.ts.map +1 -0
  353. package/out/src/{wof-json.js → utils/wof-json.js} +5 -8
  354. package/out/src/utils/wof-json.js.map +1 -0
  355. package/package.json +66 -20
  356. package/src/adapters/ban/adapter.ts +58 -68
  357. package/src/adapters/ban/street-decompose.ts +17 -25
  358. package/src/adapters/fcc-bdc/adapter.ts +5 -36
  359. package/src/adapters/geonames/adapter.ts +67 -76
  360. package/src/adapters/geonames-postal/adapter.ts +45 -54
  361. package/src/adapters/gnaf/adapter.ts +7 -11
  362. package/src/adapters/gnaf/assemble.ts +7 -11
  363. package/src/adapters/index.ts +3 -2
  364. package/src/adapters/openaddresses/adapter.ts +6 -12
  365. package/src/adapters/overture/adapter.ts +6 -10
  366. package/src/adapters/state-hi-schools/adapter.ts +53 -77
  367. package/src/adapters/state-ia-contractors/adapter.ts +55 -80
  368. package/src/adapters/state-ny-notaries/adapter.ts +64 -89
  369. package/src/adapters/state-tx-notaries/adapter.ts +63 -88
  370. package/src/adapters/synth-po-box/adapter.ts +12 -23
  371. package/src/adapters/tiger/adapter.ts +4 -3
  372. package/src/adapters/tiger/street-decompose.ts +21 -31
  373. package/src/adapters/usgov-hrsa-fqhc/adapter.ts +64 -88
  374. package/src/adapters/usgov-imls-pls/adapter.ts +57 -86
  375. package/src/adapters/usgov-irs-bmf/adapter.ts +55 -66
  376. package/src/adapters/usgov-nad/adapter.ts +7 -11
  377. package/src/adapters/usgov-nppes/adapter.ts +54 -79
  378. package/src/adapters/usgov-samhsa-treatment-locator/adapter.ts +48 -75
  379. package/src/{adapter.ts → adapters/utils/index.ts} +59 -3
  380. package/src/adapters/wof-admin-jp/adapter.ts +35 -9
  381. package/src/adapters/wof-admin-json/adapter.ts +3 -4
  382. package/src/adapters/wof-postalcode-json/adapter.ts +3 -4
  383. package/src/build.ts +12 -11
  384. package/src/index.ts +3 -17
  385. package/src/name-prone-us-suffixes.ts +12 -0
  386. package/src/parquet-wrapper/reader.ts +19 -0
  387. package/src/parquet-wrapper/schema.ts +4 -0
  388. package/src/runner.ts +7 -2
  389. package/src/shard-recipes/anchor-absorption.ts +5 -4
  390. package/src/shard-recipes/boundary-stress.ts +6 -2
  391. package/src/shard-recipes/country-balanced.ts +77 -91
  392. package/src/shard-recipes/fr-admin-split.ts +3 -3
  393. package/src/shard-recipes/fr-fragment.ts +10 -7
  394. package/src/shard-recipes/fr-lieudit.ts +3 -3
  395. package/src/shard-recipes/fr-order.ts +18 -83
  396. package/src/shard-recipes/german.ts +17 -80
  397. package/src/shard-recipes/house-venue.ts +2 -1
  398. package/src/shard-recipes/index.ts +4 -1
  399. package/src/shard-recipes/intersection.ts +37 -74
  400. package/src/shard-recipes/locale.ts +31 -39
  401. package/src/shard-recipes/no-fragment.ts +10 -7
  402. package/src/shard-recipes/no-street-led.ts +10 -7
  403. package/src/shard-recipes/no-street.ts +2 -1
  404. package/src/shard-recipes/po-box-cedex.ts +127 -161
  405. package/src/shard-recipes/po-box.ts +6 -1
  406. package/src/shard-recipes/scaffold.ts +125 -27
  407. package/src/shard-recipes/street-affix.ts +335 -134
  408. package/src/shard-recipes/street-bare.ts +4 -3
  409. package/src/shard-recipes/street.ts +3 -2
  410. package/src/shard-recipes/sub-venue-sources.ts +895 -0
  411. package/src/shard-recipes/sub-venue.ts +982 -0
  412. package/src/shard-recipes/unit.ts +27 -85
  413. package/src/{synthesize-boundary-stress.ts → synthesizers/boundary-stress.ts} +2 -2
  414. package/src/{synthesize-german.ts → synthesizers/german.ts} +2 -2
  415. package/src/{synthesize-house-venue.ts → synthesizers/house-venue.ts} +4 -4
  416. package/src/{synthesize-intersection.ts → synthesizers/intersection.ts} +1 -1
  417. package/src/{synthesize-no-street.ts → synthesizers/no-street.ts} +2 -2
  418. package/src/{synthesize-po-box.ts → synthesizers/po-box.ts} +1 -1
  419. package/src/{synthesize-street.ts → synthesizers/street.ts} +2 -2
  420. package/src/{synthesize.ts → synthesizers/utils.ts} +4 -18
  421. package/src/tools/align-shard.ts +4 -7
  422. package/src/tools/audit.ts +6 -5
  423. package/src/tools/corpus-stats.ts +25 -33
  424. package/src/tools/fetch/download.ts +7 -5
  425. package/src/tools/fetch/imls-pls.ts +4 -18
  426. package/src/tools/fetch/index.ts +14 -1
  427. package/src/tools/fetch/nad.ts +1 -1
  428. package/src/tools/fetch/nppes.ts +9 -16
  429. package/src/tools/fetch/openaddresses.ts +1 -1
  430. package/src/tools/fetch/ourairports.ts +166 -0
  431. package/src/tools/fetch/wikidata-subvenue.ts +386 -0
  432. package/src/tools/golden-expand.ts +18 -13
  433. package/src/tools/golden-promote.ts +15 -6
  434. package/src/tools/golden-relabel-street.ts +685 -0
  435. package/src/tools/index.ts +4 -0
  436. package/src/tools/jsonl-to-parquet.ts +3 -2
  437. package/src/tools/lint-shard-vocab.ts +3 -2
  438. package/src/tools/lint-shard.ts +33 -36
  439. package/src/tools/overlay-manifest.ts +2 -1
  440. package/src/tools/overture-subvenue.ts +215 -0
  441. package/src/tools/shard-kryptonite.ts +8 -9
  442. package/src/tools/shard-translit.ts +22 -11
  443. package/src/tools/sub-venue-lexicon.ts +1250 -0
  444. package/src/tools/sub-venue-promotions.ts +330 -0
  445. package/src/{align.ts → utils/align.ts} +1 -1
  446. package/src/{golden.ts → utils/golden.ts} +25 -11
  447. package/src/utils/index.ts +14 -0
  448. package/src/{parquet.ts → utils/parquet.ts} +5 -19
  449. package/src/{split.ts → utils/split.ts} +8 -2
  450. package/src/{wof-json.ts → utils/wof-json.ts} +5 -8
  451. package/out/src/adapter.d.ts.map +0 -1
  452. package/out/src/adapter.js.map +0 -1
  453. package/out/src/align.d.ts.map +0 -1
  454. package/out/src/align.js.map +0 -1
  455. package/out/src/format.d.ts +0 -14
  456. package/out/src/format.d.ts.map +0 -1
  457. package/out/src/format.js +0 -14
  458. package/out/src/format.js.map +0 -1
  459. package/out/src/golden.d.ts.map +0 -1
  460. package/out/src/golden.js.map +0 -1
  461. package/out/src/license.d.ts.map +0 -1
  462. package/out/src/license.js.map +0 -1
  463. package/out/src/parquet.d.ts.map +0 -1
  464. package/out/src/parquet.js.map +0 -1
  465. package/out/src/split.d.ts.map +0 -1
  466. package/out/src/split.js.map +0 -1
  467. package/out/src/synthesize-anchor-absorption.d.ts.map +0 -1
  468. package/out/src/synthesize-anchor-absorption.js.map +0 -1
  469. package/out/src/synthesize-boundary-stress.d.ts.map +0 -1
  470. package/out/src/synthesize-boundary-stress.js.map +0 -1
  471. package/out/src/synthesize-german.d.ts.map +0 -1
  472. package/out/src/synthesize-german.js.map +0 -1
  473. package/out/src/synthesize-house-venue.d.ts.map +0 -1
  474. package/out/src/synthesize-house-venue.js.map +0 -1
  475. package/out/src/synthesize-intersection.d.ts.map +0 -1
  476. package/out/src/synthesize-intersection.js.map +0 -1
  477. package/out/src/synthesize-no-street.d.ts.map +0 -1
  478. package/out/src/synthesize-no-street.js.map +0 -1
  479. package/out/src/synthesize-po-box.d.ts.map +0 -1
  480. package/out/src/synthesize-po-box.js.map +0 -1
  481. package/out/src/synthesize-street.d.ts.map +0 -1
  482. package/out/src/synthesize-street.js.map +0 -1
  483. package/out/src/synthesize.d.ts.map +0 -1
  484. package/out/src/synthesize.js.map +0 -1
  485. package/out/src/tokenize.d.ts.map +0 -1
  486. package/out/src/tokenize.js.map +0 -1
  487. package/out/src/wof-json.d.ts.map +0 -1
  488. package/out/src/wof-json.js.map +0 -1
  489. package/src/format.ts +0 -14
  490. /package/out/src/{align.js → utils/align.js} +0 -0
  491. /package/out/src/{license.d.ts → utils/license.d.ts} +0 -0
  492. /package/out/src/{license.js → utils/license.js} +0 -0
  493. /package/out/src/{tokenize.d.ts → utils/tokenize.d.ts} +0 -0
  494. /package/out/src/{tokenize.js → utils/tokenize.js} +0 -0
  495. /package/src/{synthesize-anchor-absorption.ts → synthesizers/anchor-absorption.ts} +0 -0
  496. /package/src/{license.ts → utils/license.ts} +0 -0
  497. /package/src/{tokenize.ts → utils/tokenize.ts} +0 -0
@@ -27,8 +27,6 @@
27
27
  * FR/DE postcode (the v0.9.8 blemish).
28
28
  */
29
29
 
30
- import { spawnSync } from "node:child_process"
31
-
32
30
  import {
33
31
  DirectionalAbbreviation,
34
32
  lookupDirectional,
@@ -39,11 +37,13 @@ import {
39
37
  US_STREET_SUFFIX_PREFERRED_ABBR,
40
38
  } from "@mailwoman/codex/us"
41
39
  import type { ComponentTag } from "@mailwoman/core/types"
40
+ import { dataRootPath } from "@mailwoman/core/utils"
41
+ import { stableSourceID } from "@mailwoman/corpus/adapters/utils"
42
+ import type { CanonicalRow } from "@mailwoman/corpus/types"
43
+ import { alignRow } from "@mailwoman/corpus/utils"
42
44
 
43
- import { stableSourceID } from "../adapter.ts"
44
- import { alignRow } from "../align.ts"
45
- import type { CanonicalRow } from "../types.ts"
46
- import { makeMulberry32, type ShardRecipe } from "./scaffold.ts"
45
+ import { NAME_PRONE_US_SUFFIXES } from "../name-prone-us-suffixes.ts"
46
+ import { makeMulberry32, readCSVRecords, readZippedCSVRecords, shardSourceID, type ShardRecipe } from "./scaffold.ts"
47
47
 
48
48
  // Same OA cache as the unit shard. Train = every NON-Vermont state; eval = Vermont (the holdout).
49
49
  /* oxlint-disable sister-software/no-unnamed-threshold -- the bare decimals below are weighted-sampler
@@ -59,16 +59,20 @@ interface USSource {
59
59
  }
60
60
 
61
61
  const TRAIN_SOURCES: readonly USSource[] = [
62
- { zip: "/tmp/oa-cache/us__ca__berkeley.zip", csv: "us/ca/berkeley.csv", region: "CA" },
63
- { zip: "/tmp/oa-cache/us__ca__marin.zip", csv: "us/ca/marin.csv", region: "CA" },
64
- { zip: "/tmp/oa-cache/us__dc__statewide.zip", csv: "us/dc/statewide.csv", region: "DC" },
65
- { zip: "/tmp/oa-cache/us__ia__statewide.zip", csv: "us/ia/statewide.csv", region: "IA" },
66
- { zip: "/tmp/oa-cache/us__il__cook.zip", csv: "us/il/cook.csv", region: "IL" },
67
- { zip: "/tmp/oa-cache/us__mt__statewide.zip", csv: "us/mt/statewide.csv", region: "MT" },
68
- { zip: "/tmp/oa-cache/us__sd__statewide.zip", csv: "us/sd/statewide.csv", region: "SD" },
62
+ { zip: dataRootPath("oa-cache", "us__ca__berkeley.zip"), csv: "us/ca/berkeley.csv", region: "CA" },
63
+ { zip: dataRootPath("oa-cache", "us__ca__marin.zip"), csv: "us/ca/marin.csv", region: "CA" },
64
+ { zip: dataRootPath("oa-cache", "us__dc__statewide.zip"), csv: "us/dc/statewide.csv", region: "DC" },
65
+ { zip: dataRootPath("oa-cache", "us__ia__statewide.zip"), csv: "us/ia/statewide.csv", region: "IA" },
66
+ { zip: dataRootPath("oa-cache", "us__il__cook.zip"), csv: "us/il/cook.csv", region: "IL" },
67
+ { zip: dataRootPath("oa-cache", "us__mt__statewide.zip"), csv: "us/mt/statewide.csv", region: "MT" },
68
+ { zip: dataRootPath("oa-cache", "us__sd__statewide.zip"), csv: "us/sd/statewide.csv", region: "SD" },
69
69
  ]
70
70
 
71
- const EVAL_SOURCE: USSource = { zip: "/tmp/oa-cache/us__vt__statewide.zip", csv: "us/vt/statewide.csv", region: "VT" }
71
+ const EVAL_SOURCE: USSource = {
72
+ zip: dataRootPath("oa-cache", "us__vt__statewide.zip"),
73
+ csv: "us/vt/statewide.csv",
74
+ region: "VT",
75
+ }
72
76
 
73
77
  // Multi-locale BALANCE sources (--multilocale-count > 0). These rows carry NO affix split — they exist
74
78
  // only to keep the postcode-ORDER distribution multi-locale. Native-order rendering mirrors
@@ -83,14 +87,38 @@ interface BalanceSource {
83
87
  }
84
88
 
85
89
  const MULTILOCALE_SOURCES: readonly BalanceSource[] = [
86
- { zip: "/tmp/oa-cache/de__sn__statewide.zip", csv: "de/sn/statewide.csv", iso2: "DE", region: "", order: "eu" },
87
- { zip: "/tmp/oa-cache/fr__countrywide.zip", csv: "fr/countrywide.csv", iso2: "FR", region: "", order: "fr" },
88
- { zip: "/tmp/oa-cache/it__countrywide.zip", csv: "it/countrywide.csv", iso2: "IT", region: "", order: "eu" },
89
- { zip: "/tmp/oa-cache/nl__countrywide.zip", csv: "nl/countrywide.csv", iso2: "NL", region: "", order: "eu" },
90
+ {
91
+ zip: dataRootPath("oa-cache", "de__sn__statewide.zip"),
92
+ csv: "de/sn/statewide.csv",
93
+ iso2: "DE",
94
+ region: "",
95
+ order: "eu",
96
+ },
97
+ {
98
+ zip: dataRootPath("oa-cache", "fr__countrywide.zip"),
99
+ csv: "fr/countrywide.csv",
100
+ iso2: "FR",
101
+ region: "",
102
+ order: "fr",
103
+ },
104
+ {
105
+ zip: dataRootPath("oa-cache", "it__countrywide.zip"),
106
+ csv: "it/countrywide.csv",
107
+ iso2: "IT",
108
+ region: "",
109
+ order: "eu",
110
+ },
111
+ {
112
+ zip: dataRootPath("oa-cache", "nl__countrywide.zip"),
113
+ csv: "nl/countrywide.csv",
114
+ iso2: "NL",
115
+ region: "",
116
+ order: "eu",
117
+ },
90
118
  ]
91
119
 
92
120
  const MULTILOCALE_EVAL_SOURCES: readonly BalanceSource[] = [
93
- { zip: "/tmp/oa-cache/de__berlin.zip", csv: "de/berlin.csv", iso2: "DE", region: "", order: "eu" },
121
+ { zip: dataRootPath("oa-cache", "de__berlin.zip"), csv: "de/berlin.csv", iso2: "DE", region: "", order: "eu" },
94
122
  ]
95
123
 
96
124
  /**
@@ -111,6 +139,7 @@ interface USTuple {
111
139
  locality: string
112
140
  region: string
113
141
  postcode: string
142
+ base_source_id: string
114
143
  }
115
144
 
116
145
  /**
@@ -131,84 +160,32 @@ interface BalanceTuple {
131
160
  */
132
161
  type Prefix = Pick<NonNullable<ReturnType<typeof matchLeadingDirectional>>, "canonical" | "abbreviation">
133
162
 
134
- /**
135
- * Minimal RFC-4180-ish splitter (handles quoted fields).
136
- */
137
- function splitCSV(line: string): string[] {
138
- const out: string[] = []
139
- let cur = ""
140
- let inQ = false
141
-
142
- for (let i = 0; i < line.length; i++) {
143
- const c = line[i]
144
-
145
- if (inQ) {
146
- if (c === '"') {
147
- if (line[i + 1] === '"') {
148
- cur += '"'
149
-
150
- i++
151
- } else {
152
- inQ = false
153
- }
154
- } else {
155
- cur += c
156
- }
157
- } else if (c === '"') {
158
- inQ = true
159
- } else if (c === ",") {
160
- out.push(cur)
161
- cur = ""
162
- } else {
163
- cur += c
164
- }
165
- }
166
-
167
- out.push(cur)
168
-
169
- return out
170
- }
171
-
172
163
  /**
173
164
  * Stream real US tuples (number/street/city/postcode) out of a cached OA zip.
174
165
  */
175
- function readTuples(source: USSource): USTuple[] {
176
- const r = spawnSync("unzip", ["-p", source.zip, source.csv], { maxBuffer: 1024 * 1024 * 1024, encoding: "buffer" })
177
-
178
- if (r.status !== 0) {
179
- console.error(` WARN: unzip failed for ${source.zip} (status ${r.status})`)
180
-
181
- return []
182
- }
183
-
184
- const lines = r.stdout.toString("utf8").split(/\r?\n/)
185
-
186
- if (lines.length < 2) return []
187
- const header = splitCSV(lines[0]!).map((h) => h.trim().toLowerCase())
188
- const idx = (name: string): number => header.indexOf(name)
189
-
190
- const iNum = idx("number"),
191
- iStreet = idx("street"),
192
- iCity = idx("city"),
193
- iPost = idx("postcode")
194
-
195
- const get = (cells: string[], i: number): string => (i >= 0 && i < cells.length ? (cells[i] ?? "").trim() : "")
166
+ async function readTuples(source: USSource): Promise<USTuple[]> {
196
167
  const tuples: USTuple[] = []
197
168
  const seen = new Set<string>()
198
169
 
199
- for (let li = 1; li < lines.length; li++) {
200
- if (!lines[li]) continue
201
- const cells = splitCSV(lines[li]!)
202
- const street = get(cells, iStreet)
203
- const locality = get(cells, iCity)
204
- const house_number = get(cells, iNum)
170
+ for await (const row of readZippedCSVRecords(source.zip, source.csv)) {
171
+ const street = row.street ?? ""
172
+ const locality = row.city ?? ""
173
+ const house_number = row.number ?? ""
205
174
 
206
175
  if (!street || !locality || !house_number) continue
207
176
  const key = `${house_number}|${street}|${locality}`.toLowerCase()
208
177
 
209
178
  if (seen.has(key)) continue
210
179
  seen.add(key)
211
- tuples.push({ house_number, street, locality, region: source.region, postcode: get(cells, iPost) })
180
+
181
+ tuples.push({
182
+ house_number,
183
+ street,
184
+ locality,
185
+ region: source.region,
186
+ postcode: row.postcode ?? "",
187
+ base_source_id: `openaddresses:${source.csv}:${key}`,
188
+ })
212
189
  }
213
190
 
214
191
  return tuples
@@ -228,7 +205,10 @@ const isSuffixOrDirectional = (word: string): boolean =>
228
205
  * Split an OA street into { prefix?, name, suffix } using the codex. Requires a trailing suffix and a non-empty name
229
206
  * that isn't itself an affix token. Returns null when the street has no usable suffix.
230
207
  */
231
- function parseStreet(street: string): { prefix: Prefix | null; name: string; suffix: string } | null {
208
+ function parseStreet(
209
+ street: string,
210
+ opts: { allowNameProneTail?: boolean } = {}
211
+ ): { prefix: Prefix | null; name: string; suffix: string } | null {
232
212
  let words = street.trim().split(/\s+/)
233
213
 
234
214
  if (words.length < 2) return null
@@ -248,11 +228,42 @@ function parseStreet(street: string): { prefix: Prefix | null; name: string; suf
248
228
  const suffix = trail.canonical
249
229
  const name = words.slice(0, -1).join(" ")
250
230
 
251
- if (!name || isSuffixOrDirectional(name)) return null
231
+ if (!name) return null
232
+
233
+ if (isSuffixOrDirectional(name)) {
234
+ const nameTail = matchTrailingSuffix(name)
235
+
236
+ if (!opts.allowNameProneTail || !nameTail || !NAME_PRONE_US_SUFFIXES.has(nameTail.canonical)) return null
237
+ }
252
238
 
253
239
  return { prefix, name, suffix }
254
240
  }
255
241
 
242
+ export type SuffixBoundaryClass = "terminal-only" | "terminal-contrast"
243
+
244
+ /**
245
+ * Classify a real street surface for #1569. `terminal-only` has an ambiguous suffix-eligible name word immediately
246
+ * before a different terminal type (`Blue Hill Rd`); `terminal-contrast` ends at the ambiguous word itself (`Sutton
247
+ * Hollow`). The contrast check intentionally wins when both final words are name-prone: only the LAST token is the
248
+ * suffix under the canonical rule.
249
+ */
250
+ export function classifySuffixBoundaryStreet(street: string): SuffixBoundaryClass | null {
251
+ const words = street.trim().split(/\s+/)
252
+
253
+ if (words.length < 2) return null
254
+ const terminal = matchTrailingSuffix(words.at(-1)!)
255
+
256
+ if (!terminal) return null
257
+
258
+ if (NAME_PRONE_US_SUFFIXES.has(terminal.canonical)) return "terminal-contrast"
259
+
260
+ if (words.length < 3) return null
261
+
262
+ const penultimate = matchTrailingSuffix(words.at(-2)!)
263
+
264
+ return penultimate && NAME_PRONE_US_SUFFIXES.has(penultimate.canonical) ? "terminal-only" : null
265
+ }
266
+
256
267
  /**
257
268
  * Render the affix-split street in random surface forms (abbrev vs expanded per affix), Title-cased.
258
269
  */
@@ -302,18 +313,65 @@ function renderStreet(
302
313
  */
303
314
  const VENUES = ["John Doe", "Jane Smith", "Acme Inc", "Wayne Enterprises", "Maria Garcia", "Riverside Clinic"]
304
315
 
316
+ /**
317
+ * Real-venue pool for the suffix-boundary v2 venue shell (corpus 0.19.0). The 2026-08-10 recipe review found the
318
+ * v0.18.x shard's venue-led rows detectable as templates (six fixed venue strings), and the frozen B1 board's failures
319
+ * concentrate in exactly that shell (rich rows 5/43 vs bare 53/65 at v4.3.3 step-40k). HRSA's health-center site file
320
+ * supplies thousands of REAL US facility names ('Alburg Health Center'-register), US-government public domain, already
321
+ * a corpus source (`usgov-hrsa-fqhc`). Kept verbatim (including the ~11% all-caps names — real register diversity);
322
+ * comma-carrying names are dropped because the venue layout uses commas as its field delimiter.
323
+ */
324
+ const VENUE_POOL_CSV = dataRootPath(
325
+ "corpus",
326
+ "sources",
327
+ "usgov-hrsa-fqhc",
328
+ "Health_Center_Service_Delivery_and_LookAlike_Sites.csv"
329
+ )
330
+
331
+ async function readVenuePool(csvPath: string): Promise<string[]> {
332
+ const seen = new Set<string>()
333
+ const pool: string[] = []
334
+
335
+ for await (const record of readCSVRecords(csvPath)) {
336
+ const name = (record["site name"] ?? record["Site Name"] ?? "").trim().replaceAll(/\s+/gu, " ")
337
+
338
+ if (name.length < 3 || name.length > 60 || name.includes(",") || !/\p{L}/u.test(name)) continue
339
+ const key = name.toLowerCase()
340
+
341
+ if (seen.has(key)) continue
342
+ seen.add(key)
343
+ pool.push(name)
344
+ }
345
+
346
+ return pool
347
+ }
348
+
305
349
  const tail = (loc: string, reg: string, pc: string): string => (pc ? `${loc}, ${reg} ${pc}` : `${loc}, ${reg}`)
306
350
 
351
+ /**
352
+ * Layout-shell options for {@link renderRow}. `cuts` are the cumulative random() cutoffs for [full, bare, street-only] —
353
+ * the remainder is the venue shell. Defaults reproduce the original street-affix distribution (40/25/20/15) with the
354
+ * six template venues.
355
+ */
356
+ interface RenderRowOpts {
357
+ venues?: readonly string[]
358
+ cuts?: readonly [number, number, number]
359
+ }
360
+
307
361
  /**
308
362
  * Embed the rendered street in a RANDOM layout so the model recognizes affixes wherever the street sits: full address,
309
363
  * bare house+street, street-only (pure affix parse), or venue-prefixed.
310
364
  */
311
- function renderRow(
365
+ export function renderRow(
312
366
  random: () => number,
313
367
  base: USTuple,
314
368
  street: string,
315
- streetComponents: Partial<Record<ComponentTag, string>>
369
+ streetComponents: Partial<Record<ComponentTag, string>>,
370
+ opts: RenderRowOpts = {}
316
371
  ): { fmt: string; raw: string; components: Partial<Record<ComponentTag, string>> } {
372
+ const venues = opts.venues ?? VENUES
373
+ const [fullCut, bareCut, streetOnlyCut] = opts.cuts ?? [0.4, 0.65, 0.85]
374
+
317
375
  const hn = base.house_number,
318
376
  loc = base.locality,
319
377
  reg = base.region,
@@ -323,17 +381,17 @@ function renderRow(
323
381
  const withRoad: Partial<Record<ComponentTag, string>> = { house_number: hn, ...streetComponents }
324
382
  const r = random()
325
383
 
326
- if (r < 0.4)
384
+ if (r < fullCut)
327
385
  return {
328
386
  fmt: "full",
329
387
  raw: `${road}, ${tail(loc, reg, pc)}`,
330
388
  components: { ...withRoad, locality: loc, region: reg, ...(pc ? { postcode: pc } : {}) },
331
389
  }
332
390
 
333
- if (r < 0.65) return { fmt: "bare", raw: road, components: withRoad }
391
+ if (r < bareCut) return { fmt: "bare", raw: road, components: withRoad }
334
392
 
335
- if (r < 0.85) return { fmt: "street-only", raw: street, components: { ...streetComponents } }
336
- const v = VENUES[Math.floor(random() * VENUES.length)]!
393
+ if (r < streetOnlyCut) return { fmt: "street-only", raw: street, components: { ...streetComponents } }
394
+ const v = venues[Math.floor(random() * venues.length)]!
337
395
 
338
396
  return {
339
397
  fmt: "venue",
@@ -343,48 +401,21 @@ function renderRow(
343
401
  }
344
402
 
345
403
  /**
346
- * Capped reader for the multi-locale BALANCE sources. The FR/IT/NL countrywide extracts are GB-scale; reading the whole
347
- * CSV blows V8's string limit, so cap the bytes with `head` (mirrors build-country-shard-balanced.mjs). Only keeps
348
- * tuples that carry a POSTCODE.
404
+ * Capped reader for the multi-locale BALANCE sources. The FR/IT/NL countrywide extracts are GB-scale, so this reads
405
+ * only as far as `limit` distinct tuples — the `break` closes the reader and releases the archive. Only keeps tuples
406
+ * that carry a POSTCODE.
349
407
  */
350
- function readBalanceTuples(source: BalanceSource, limit: number): BalanceTuple[] {
351
- const maxLines = Math.max(limit * 8, 20_000) + 1
352
-
353
- const r = spawnSync("bash", ["-c", `unzip -p "${source.zip}" "${source.csv}" | head -n ${maxLines}`], {
354
- maxBuffer: 1024 * 1024 * 1024,
355
- encoding: "buffer",
356
- })
357
-
358
- if (r.status !== 0) {
359
- console.error(` WARN: unzip failed for ${source.zip} (status ${r.status})`)
360
-
361
- return []
362
- }
363
-
364
- const lines = r.stdout.toString("utf8").split(/\r?\n/)
365
-
366
- if (lines.length < 2) return []
367
- const header = splitCSV(lines[0]!).map((h) => h.trim().toLowerCase())
368
- const idx = (n: string): number => header.indexOf(n)
369
-
370
- const iNum = idx("number"),
371
- iStreet = idx("street"),
372
- iCity = idx("city"),
373
- iRegion = idx("region"),
374
- iPost = idx("postcode")
375
-
376
- const get = (cells: string[], i: number): string => (i >= 0 && i < cells.length ? (cells[i] ?? "").trim() : "")
408
+ async function readBalanceTuples(source: BalanceSource, limit: number): Promise<BalanceTuple[]> {
377
409
  const tuples: BalanceTuple[] = []
378
410
  const seen = new Set<string>()
379
411
 
380
- for (let li = 1; li < lines.length && tuples.length < limit; li++) {
381
- if (!lines[li]) continue
382
- const cells = splitCSV(lines[li]!)
412
+ for await (const row of readZippedCSVRecords(source.zip, source.csv)) {
413
+ if (tuples.length >= limit) break
383
414
 
384
- const street = get(cells, iStreet),
385
- locality = get(cells, iCity),
386
- house_number = get(cells, iNum),
387
- postcode = get(cells, iPost)
415
+ const street = row.street ?? "",
416
+ locality = row.city ?? "",
417
+ house_number = row.number ?? "",
418
+ postcode = row.postcode ?? ""
388
419
 
389
420
  if (!street || !locality || !house_number || !postcode) continue // postcode is required for balance
390
421
  const key = `${house_number}|${street}|${locality}`.toLowerCase()
@@ -396,7 +427,7 @@ function readBalanceTuples(source: BalanceSource, limit: number): BalanceTuple[]
396
427
  house_number,
397
428
  street,
398
429
  locality,
399
- region: get(cells, iRegion) || source.region,
430
+ region: row.region || source.region,
400
431
  postcode,
401
432
  iso2: source.iso2,
402
433
  order: source.order,
@@ -449,7 +480,7 @@ export const streetAffixRecipe: ShardRecipe = {
449
480
  const pool: USTuple[] = []
450
481
 
451
482
  for (const s of sources) {
452
- const t = readTuples(s)
483
+ const t = await readTuples(s)
453
484
 
454
485
  console.error(` ${s.csv}: ${t.length} unique tuples`)
455
486
 
@@ -459,7 +490,7 @@ export const streetAffixRecipe: ShardRecipe = {
459
490
  }
460
491
 
461
492
  if (!pool.length) {
462
- throw new Error("No US tuples found — are the cached OA zips present in /tmp/oa-cache?")
493
+ throw new Error(`No US tuples found — are the cached OA zips present in ${dataRootPath("oa-cache")}?`)
463
494
  }
464
495
 
465
496
  let emitted = 0
@@ -550,7 +581,7 @@ export const streetAffixRecipe: ShardRecipe = {
550
581
  const mlPool: BalanceTuple[] = []
551
582
 
552
583
  for (const s of mlSources) {
553
- const t = readBalanceTuples(s, perSource)
584
+ const t = await readBalanceTuples(s, perSource)
554
585
 
555
586
  console.error(` balance ${s.csv} (${s.iso2}): ${t.length} tuples`)
556
587
 
@@ -622,3 +653,173 @@ export const streetAffixRecipe: ShardRecipe = {
622
653
  return { emitted: emitted + balanceEmitted, skipped: skipped + balanceSkipped }
623
654
  },
624
655
  }
656
+
657
+ /**
658
+ * #1569 root-fix shard. Both classes come from real non-Vermont OA streets and use the affix recipe's existing layout
659
+ * diversity. v4.3.1 makes terminal-only 80% of the mix: the first 40/60 run moved a 100-row TRAIN sample only 4→11
660
+ * while contrast was already 95/100 before training (93/100 after). Post-run audit found that the global affix relabel
661
+ * pass corrupts many already-decomposed target rows into double suffixes; do not retrain this recipe until relabel is
662
+ * idempotent over a decomposed street family. The 20% contrast leg remains explicit, additive to the already-strong
663
+ * base distribution, and B2 still gates it unchanged.
664
+ *
665
+ * V2 (corpus 0.19.0, 2026-08-10 recipe review): the v4.3.3 board split (rich venue-led rows 5/43 vs bare 53/65) showed
666
+ * the model separating template rows from real ones, and the venue shell was the giveaway — six fixed venue strings. v2
667
+ * draws the venue shell from thousands of REAL HRSA facility names and raises its share (venue 30%, full 35%, bare 20%,
668
+ * street-only 15%). Dose policy moved to the recipe's config side: weight ≤4 effective passes per run (Muennighoff
669
+ * 2023's repetition knee) and the source is excluded from the augmentation pool — see the v4.4.0 config.
670
+ */
671
+ export const suffixBoundaryRecipe: ShardRecipe = {
672
+ name: "suffix-boundary",
673
+ description: "US #1569 suffix boundary: OA terminal-only positives + over-sampled terminal contrasts",
674
+ mode: "generate",
675
+ async run(opts, write) {
676
+ const random = makeMulberry32(opts.seed)
677
+ const count = opts.count ?? 30_000
678
+ const source = opts.sourceName ?? "synth-suffix-boundary"
679
+ const sources = opts.golden ? [EVAL_SOURCE] : TRAIN_SOURCES
680
+
681
+ // v2 venue shell: real facility names, loud when the source file is missing or thin.
682
+ const venuePool = await readVenuePool(VENUE_POOL_CSV)
683
+
684
+ if (venuePool.length < 500) {
685
+ throw new Error(
686
+ `suffix-boundary v2 venue pool too thin: ${venuePool.length} names from ${VENUE_POOL_CSV} — ` +
687
+ "is the usgov-hrsa-fqhc source present?"
688
+ )
689
+ }
690
+
691
+ const shell: RenderRowOpts = { venues: venuePool, cuts: [0.35, 0.55, 0.7] }
692
+
693
+ const pool: Record<SuffixBoundaryClass, USTuple[]> = {
694
+ "terminal-only": [],
695
+ "terminal-contrast": [],
696
+ }
697
+
698
+ for (const s of sources) {
699
+ for (const tuple of await readTuples(s)) {
700
+ const rowClass = classifySuffixBoundaryStreet(tuple.street)
701
+
702
+ if (rowClass) {
703
+ pool[rowClass].push(tuple)
704
+ }
705
+ }
706
+ }
707
+
708
+ if (!pool["terminal-only"].length || !pool["terminal-contrast"].length) {
709
+ throw new Error(
710
+ `Suffix-boundary source class missing: terminal-only=${pool["terminal-only"].length}, ` +
711
+ `terminal-contrast=${pool["terminal-contrast"].length}`
712
+ )
713
+ }
714
+
715
+ let emitted = 0
716
+ let skipped = 0
717
+ const classCounts: Record<SuffixBoundaryClass, number> = { "terminal-only": 0, "terminal-contrast": 0 }
718
+ const terminalOnlyTarget = Math.round(count * 0.8)
719
+
720
+ const classTargets: Record<SuffixBoundaryClass, number> = {
721
+ "terminal-only": terminalOnlyTarget,
722
+ "terminal-contrast": count - terminalOnlyTarget,
723
+ }
724
+
725
+ const formatCounts: Record<string, number> = {}
726
+ let stemPairs = 0
727
+
728
+ const emitOne = (
729
+ rowClass: SuffixBoundaryClass,
730
+ base: USTuple,
731
+ parsed: NonNullable<ReturnType<typeof parseStreet>>,
732
+ method: string
733
+ ): boolean => {
734
+ const { street, components: streetComponents } = renderStreet(random, parsed)
735
+ const { fmt, raw, components } = renderRow(random, base, street, streetComponents, shell)
736
+
737
+ const canonical: CanonicalRow = {
738
+ raw,
739
+ components,
740
+ country: "US",
741
+ locale: "en-US",
742
+ source,
743
+ source_id: shardSourceID(source, { ...components, class: rowClass, n: String(emitted) }),
744
+ corpus_version: "0.19.0",
745
+ license:
746
+ "OpenAddresses US (non-VT) skeletons; terminal suffix labels via USPS Pub-28 codex; venue shell from HRSA site names (US public domain)",
747
+ }
748
+
749
+ const aligned = alignRow(canonical)
750
+
751
+ if (aligned.kind !== "labeled" || !aligned.row) {
752
+ skipped++
753
+
754
+ return false
755
+ }
756
+
757
+ write(JSON.stringify({ ...aligned.row, synth_method: method, synth_base_id: base.base_source_id }) + "\n")
758
+
759
+ classCounts[rowClass]++
760
+ formatCounts[fmt] = (formatCounts[fmt] ?? 0) + 1
761
+
762
+ emitted++
763
+
764
+ return true
765
+ }
766
+
767
+ // oxlint-disable-next-line eslint/no-unmodified-loop-condition -- emitted/skipped advance inside emitOne (a closure); the rule cannot see through the call
768
+ while (emitted < count && emitted + skipped < count * 10) {
769
+ const terminalOnlyOpen = classCounts["terminal-only"] < classTargets["terminal-only"]
770
+ const terminalContrastOpen = classCounts["terminal-contrast"] < classTargets["terminal-contrast"]
771
+
772
+ const rowClass: SuffixBoundaryClass =
773
+ terminalOnlyOpen && terminalContrastOpen
774
+ ? random() < 0.8
775
+ ? "terminal-only"
776
+ : "terminal-contrast"
777
+ : terminalContrastOpen
778
+ ? "terminal-contrast"
779
+ : "terminal-only"
780
+
781
+ const classPool = pool[rowClass]
782
+ const base = classPool[Math.floor(random() * classPool.length)]!
783
+ const parsed = parseStreet(base.street, { allowNameProneTail: rowClass === "terminal-only" })
784
+
785
+ if (!parsed) {
786
+ skipped++
787
+
788
+ continue
789
+ }
790
+
791
+ if (!emitOne(rowClass, base, parsed, `suffix-boundary:${rowClass}`)) continue
792
+
793
+ // Stem-pair hard negative (v2, 2026-08-10 recipe review): after a terminal-only row
794
+ // ('Menlo Park' + 'Road'), also emit the SAME stem without its true suffix as a
795
+ // contrast row ('Menlo' + 'Park' under the canonical last-token rule). Sharing the stem
796
+ // forces the model to key on the licensing evidence — the trailing true suffix — rather
797
+ // than on name identity. Dose-neutral: these fill the existing 20% contrast target.
798
+ if (
799
+ rowClass === "terminal-only" &&
800
+ classCounts["terminal-contrast"] < classTargets["terminal-contrast"] &&
801
+ emitted < count &&
802
+ random() < 0.5
803
+ ) {
804
+ const stemParsed = parseStreet(parsed.name)
805
+
806
+ if (
807
+ stemParsed &&
808
+ emitOne("terminal-contrast", base, stemParsed, "suffix-boundary:terminal-contrast:stem-pair")
809
+ ) {
810
+ stemPairs++
811
+ }
812
+ }
813
+ }
814
+
815
+ console.error(
816
+ `Done: emitted ${emitted}, skipped ${skipped}; source pools ${JSON.stringify({
817
+ terminalOnly: pool["terminal-only"].length,
818
+ terminalContrast: pool["terminal-contrast"].length,
819
+ })}; classes ${JSON.stringify(classCounts)}; stem pairs ${stemPairs}; ` +
820
+ `venue pool ${venuePool.length}; formats ${JSON.stringify(formatCounts)}`
821
+ )
822
+
823
+ return { emitted, skipped }
824
+ },
825
+ }
@@ -17,9 +17,10 @@
17
17
  * reproduces the legacy run byte-for-byte.
18
18
  */
19
19
 
20
- import { alignRow } from "../align.ts"
21
- import { DEFAULT_US_BASES } from "../synthesize-intersection.ts"
22
- import { synthesizeStreetRow, type StreetBaseTuple } from "../synthesize-street.ts"
20
+ import { DEFAULT_US_BASES } from "@mailwoman/corpus/synthesizers/intersection"
21
+ import { synthesizeStreetRow, type StreetBaseTuple } from "@mailwoman/corpus/synthesizers/street"
22
+ import { alignRow } from "@mailwoman/corpus/utils"
23
+
23
24
  import { makeMulberry32, shardSourceID, type CanonicalShardRow, type ShardRecipe } from "./scaffold.ts"
24
25
 
25
26
  /**
@@ -7,8 +7,9 @@
7
7
  * {@link synthesizeStreetRow} → aligned LabeledRow. Ported from scripts/build-street-shard.mjs.
8
8
  */
9
9
 
10
- import { stableSourceID } from "../adapter.ts"
11
- import { synthesizeStreetRow, type StreetBaseTuple } from "../synthesize-street.ts"
10
+ import { stableSourceID } from "@mailwoman/corpus/adapters/utils"
11
+ import { synthesizeStreetRow, type StreetBaseTuple } from "@mailwoman/corpus/synthesizers/street"
12
+
12
13
  import { alignAndWrite, makeLcg, readTuples, type ShardRecipe } from "./scaffold.ts"
13
14
 
14
15
  /**