Skip to content
SlashClone

Enrichment waterfall planner

@gtmengineer/enrichment-waterfall-planner

Plan an enrichment waterfall with real math. Give it your providers with cost, match rate, and accuracy (or a sample test run), and it tries every order to find the cheapest setup that hits your fill target, with cost per usable record, the marginal cost of each step, and where to stop. Vendor-neutral.

SkillDatav1.0.0 (latest)Tested
Activity
New: be the first to try it
  1. 1{
  2. 2 "name": "enrichment-waterfall-planner",
  3. 3 "owner": "gtmengineer",
  4. 4 "version": "1.0.0",
  5. 5 "type": "skill",
  6. 6 "description": "Plan an enrichment waterfall with real math. Give it your providers with cost, match rate, and accuracy (or a sample test run), and it tries every order to find the cheapest setup that hits your fill target, with cost per usable record, the marginal cost of each step, and where to stop. Vendor-neutral.",
  7. 7 "category": "data",
  8. 8 "license": "MIT",
  9. 9 "compat": [
  10. 10 "claude-code"
  11. 11 ],
  12. 12 "permissions": {
  13. 13 "shell": [
  14. 14 "node scripts/waterfall.mjs"
  15. 15 ],
  16. 16 "files": {
  17. 17 "read": [
  18. 18 "./data/**"
  19. 19 ],
  20. 20 "write": [
  21. 21 "./data/**",
  22. 22 "./out/**"
  23. 23 ]
  24. 24 }
  25. 25 },
  26. 26 "tests": {
  27. 27 "cases": [
  28. 28 {
  29. 29 "name": "estimated rates with verification and a fill target",
  30. 30 "inputs": {
  31. 31 "data/providers.json": "{\n \"data_point\": \"verified work email\",\n \"records\": 10000,\n \"min_fill_rate\": 0.7,\n \"max_cost_per_record\": 1.0,\n \"verify\": { \"cost\": 0.004 },\n \"providers\": [\n { \"name\": \"provider-a\", \"cost\": 0.08, \"charge\": \"per_lookup\", \"match_rate\": 0.45, \"accuracy\": 0.92 },\n { \"name\": \"provider-b\", \"cost\": 0.30, \"charge\": \"per_match\", \"match_rate\": 0.55, \"accuracy\": 0.97 },\n { \"name\": \"provider-c\", \"cost\": 0.03, \"charge\": \"per_lookup\", \"match_rate\": 0.25, \"accuracy\": 0.80 },\n { \"name\": \"provider-d\", \"cost\": 0.50, \"charge\": \"per_match\", \"match_rate\": 0.35, \"accuracy\": 0.99 }\n ]\n}\n"
  32. 32 },
  33. 33 "checks": [
  34. 34 {
  35. 35 "type": "command_exits_zero",
  36. 36 "command": "node scripts/waterfall.mjs data/providers.json --out out"
  37. 37 },
  38. 38 {
  39. 39 "type": "file_contains",
  40. 40 "path": "out/waterfall.md",
  41. 41 "text": "**Recommended order: provider-c > provider-a > provider-b**"
  42. 42 },
  43. 43 {
  44. 44 "type": "output_matches_schema",
  45. 45 "path": "out/waterfall.json",
  46. 46 "schema": {
  47. 47 "type": "object",
  48. 48 "required": [
  49. 49 "recommended",
  50. 50 "best_by_depth",
  51. 51 "provider_rates"
  52. 52 ],
  53. 53 "properties": {
  54. 54 "recommended": {
  55. 55 "type": "object",
  56. 56 "properties": {
  57. 57 "order": {
  58. 58 "const": [
  59. 59 "provider-c",
  60. 60 "provider-a",
  61. 61 "provider-b"
  62. 62 ]
  63. 63 },
  64. 64 "fill_rate": {
  65. 65 "const": 0.781
  66. 66 },
  67. 67 "total_cost": {
  68. 68 "const": 1748.23
  69. 69 },
  70. 70 "steps": {
  71. 71 "type": "array",
  72. 72 "minItems": 3,
  73. 73 "maxItems": 3
  74. 74 }
  75. 75 }
  76. 76 },
  77. 77 "best_by_depth": {
  78. 78 "type": "array",
  79. 79 "minItems": 4,
  80. 80 "maxItems": 4
  81. 81 }
  82. 82 }
  83. 83 }
  84. 84 }
  85. 85 ]
  86. 86 },
  87. 87 {
  88. 88 "name": "rates and overlap measured from a sample run",
  89. 89 "inputs": {
  90. 90 "data/providers.json": "{\n \"data_point\": \"verified work email\",\n \"records\": 10000,\n \"min_fill_rate\": 0.7,\n \"max_cost_per_record\": 1.0,\n \"verify\": { \"cost\": 0.004 },\n \"providers\": [\n { \"name\": \"provider-a\", \"cost\": 0.08, \"charge\": \"per_lookup\", \"match_rate\": 0.45, \"accuracy\": 0.92 },\n { \"name\": \"provider-b\", \"cost\": 0.30, \"charge\": \"per_match\", \"match_rate\": 0.55, \"accuracy\": 0.97 },\n { \"name\": \"provider-c\", \"cost\": 0.03, \"charge\": \"per_lookup\", \"match_rate\": 0.25, \"accuracy\": 0.80 },\n { \"name\": \"provider-d\", \"cost\": 0.50, \"charge\": \"per_match\", \"match_rate\": 0.35, \"accuracy\": 0.99 }\n ]\n}\n",
  91. 91 "data/sample.csv": "record_id,provider-a,provider-b,provider-c,provider-d\n1,x,x,,x\n2,x,,x,\n3,,x,,x\n4,,x,x,\n5,x,x,,\n6,,,x,x\n7,,,,\n8,x,x,x,x\n9,,x,,\n10,,,,x\n"
  92. 92 },
  93. 93 "checks": [
  94. 94 {
  95. 95 "type": "command_exits_zero",
  96. 96 "command": "node scripts/waterfall.mjs data/providers.json --sample data/sample.csv --out out"
  97. 97 },
  98. 98 {
  99. 99 "type": "output_matches_schema",
  100. 100 "path": "out/waterfall.json",
  101. 101 "schema": {
  102. 102 "type": "object",
  103. 103 "properties": {
  104. 104 "source": {
  105. 105 "const": "sample of 10 records"
  106. 106 },
  107. 107 "recommended": {
  108. 108 "type": "object",
  109. 109 "properties": {
  110. 110 "fill_rate": {
  111. 111 "const": 0.771
  112. 112 },
  113. 113 "total_cost": {
  114. 114 "const": 1591.9
  115. 115 }
  116. 116 }
  117. 117 },
  118. 118 "provider_rates": {
  119. 119 "type": "array",
  120. 120 "items": {
  121. 121 "type": "object",
  122. 122 "required": [
  123. 123 "name",
  124. 124 "match_rate"
  125. 125 ]
  126. 126 }
  127. 127 }
  128. 128 }
  129. 129 }
  130. 130 }
  131. 131 ]
  132. 132 },
  133. 133 {
  134. 134 "name": "no target: cheap per-lookup provider goes before the per-match one",
  135. 135 "inputs": {
  136. 136 "data/p.json": "{\"records\":100,\"providers\":[{\"name\":\"pricey-per-match\",\"cost\":1,\"charge\":\"per_match\",\"match_rate\":0.5},{\"name\":\"cheap-per-lookup\",\"cost\":0.1,\"charge\":\"per_lookup\",\"match_rate\":0.5}]}"
  137. 137 },
  138. 138 "checks": [
  139. 139 {
  140. 140 "type": "command_exits_zero",
  141. 141 "command": "node scripts/waterfall.mjs data/p.json --out out"
  142. 142 },
  143. 143 {
  144. 144 "type": "output_matches_schema",
  145. 145 "path": "out/waterfall.json",
  146. 146 "schema": {
  147. 147 "type": "object",
  148. 148 "properties": {
  149. 149 "verification": {
  150. 150 "const": false
  151. 151 },
  152. 152 "recommended": {
  153. 153 "type": "object",
  154. 154 "properties": {
  155. 155 "order": {
  156. 156 "const": [
  157. 157 "cheap-per-lookup",
  158. 158 "pricey-per-match"
  159. 159 ]
  160. 160 },
  161. 161 "fill_rate": {
  162. 162 "const": 0.75
  163. 163 },
  164. 164 "total_cost": {
  165. 165 "const": 35
  166. 166 }
  167. 167 }
  168. 168 }
  169. 169 }
  170. 170 }
  171. 171 }
  172. 172 ]
  173. 173 }
  174. 174 ]
  175. 175 }