bad-research 0.1.0__tar.gz

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 (338) hide show
  1. bad_research-0.1.0/.gitignore +25 -0
  2. bad_research-0.1.0/LICENSE +21 -0
  3. bad_research-0.1.0/PKG-INFO +143 -0
  4. bad_research-0.1.0/README.md +75 -0
  5. bad_research-0.1.0/assets/banner.png +0 -0
  6. bad_research-0.1.0/docs/HOW_IT_WORKS.md +142 -0
  7. bad_research-0.1.0/golden-eval-report.json +179 -0
  8. bad_research-0.1.0/pyproject.toml +187 -0
  9. bad_research-0.1.0/src/bad_research/__init__.py +3 -0
  10. bad_research-0.1.0/src/bad_research/__main__.py +5 -0
  11. bad_research-0.1.0/src/bad_research/_banner.py +33 -0
  12. bad_research-0.1.0/src/bad_research/browse/__init__.py +55 -0
  13. bad_research-0.1.0/src/bad_research/browse/agent_browser.py +470 -0
  14. bad_research-0.1.0/src/bad_research/browse/aql.py +476 -0
  15. bad_research-0.1.0/src/bad_research/browse/base.py +76 -0
  16. bad_research-0.1.0/src/bad_research/browse/cache.py +45 -0
  17. bad_research-0.1.0/src/bad_research/browse/extract_llm.py +113 -0
  18. bad_research-0.1.0/src/bad_research/browse/ladder.py +216 -0
  19. bad_research-0.1.0/src/bad_research/calibrate/__init__.py +51 -0
  20. bad_research-0.1.0/src/bad_research/calibrate/baselines.py +74 -0
  21. bad_research-0.1.0/src/bad_research/calibrate/constants.py +39 -0
  22. bad_research-0.1.0/src/bad_research/calibrate/cost.py +122 -0
  23. bad_research-0.1.0/src/bad_research/calibrate/golden/01_causal_light.json +23 -0
  24. bad_research-0.1.0/src/bad_research/calibrate/golden/02_comparison.json +22 -0
  25. bad_research-0.1.0/src/bad_research/calibrate/golden/03_multidomain_full.json +24 -0
  26. bad_research-0.1.0/src/bad_research/calibrate/golden/04_contested_argumentative.json +22 -0
  27. bad_research-0.1.0/src/bad_research/calibrate/golden/05_definitional.json +21 -0
  28. bad_research-0.1.0/src/bad_research/calibrate/golden/06_recency_temporal.json +22 -0
  29. bad_research-0.1.0/src/bad_research/calibrate/golden/07_breadth_list.json +22 -0
  30. bad_research-0.1.0/src/bad_research/calibrate/golden/08_numeric_precise.json +22 -0
  31. bad_research-0.1.0/src/bad_research/calibrate/golden.py +324 -0
  32. bad_research-0.1.0/src/bad_research/calibrate/harness.py +152 -0
  33. bad_research-0.1.0/src/bad_research/calibrate/judge.py +204 -0
  34. bad_research-0.1.0/src/bad_research/calibrate/runner.py +55 -0
  35. bad_research-0.1.0/src/bad_research/cli/__init__.py +70 -0
  36. bad_research-0.1.0/src/bad_research/cli/_output.py +29 -0
  37. bad_research-0.1.0/src/bad_research/cli/calibrate.py +212 -0
  38. bad_research-0.1.0/src/bad_research/cli/doctor.py +121 -0
  39. bad_research-0.1.0/src/bad_research/cli/fetch.py +80 -0
  40. bad_research-0.1.0/src/bad_research/cli/install.py +63 -0
  41. bad_research-0.1.0/src/bad_research/cli/research.py +523 -0
  42. bad_research-0.1.0/src/bad_research/config.py +121 -0
  43. bad_research-0.1.0/src/bad_research/core/__init__.py +1 -0
  44. bad_research-0.1.0/src/bad_research/core/agent_docs.py +204 -0
  45. bad_research-0.1.0/src/bad_research/core/config.py +113 -0
  46. bad_research-0.1.0/src/bad_research/core/db.py +171 -0
  47. bad_research-0.1.0/src/bad_research/core/enrich.py +110 -0
  48. bad_research-0.1.0/src/bad_research/core/fetcher.py +383 -0
  49. bad_research-0.1.0/src/bad_research/core/frontmatter.py +55 -0
  50. bad_research-0.1.0/src/bad_research/core/hooks.py +3738 -0
  51. bad_research-0.1.0/src/bad_research/core/linker.py +138 -0
  52. bad_research-0.1.0/src/bad_research/core/migrations.py +254 -0
  53. bad_research-0.1.0/src/bad_research/core/note.py +129 -0
  54. bad_research-0.1.0/src/bad_research/core/patterns.py +94 -0
  55. bad_research-0.1.0/src/bad_research/core/similarity.py +81 -0
  56. bad_research-0.1.0/src/bad_research/core/sync.py +372 -0
  57. bad_research-0.1.0/src/bad_research/core/templates.py +209 -0
  58. bad_research-0.1.0/src/bad_research/core/vault.py +168 -0
  59. bad_research-0.1.0/src/bad_research/embed/__init__.py +7 -0
  60. bad_research-0.1.0/src/bad_research/embed/base.py +45 -0
  61. bad_research-0.1.0/src/bad_research/embed/bge_local.py +38 -0
  62. bad_research-0.1.0/src/bad_research/export/__init__.py +1 -0
  63. bad_research-0.1.0/src/bad_research/funnel/__init__.py +30 -0
  64. bad_research-0.1.0/src/bad_research/funnel/_async.py +28 -0
  65. bad_research-0.1.0/src/bad_research/funnel/canonical.py +38 -0
  66. bad_research-0.1.0/src/bad_research/funnel/config.py +72 -0
  67. bad_research-0.1.0/src/bad_research/funnel/dedup.py +76 -0
  68. bad_research-0.1.0/src/bad_research/funnel/fanout.py +93 -0
  69. bad_research-0.1.0/src/bad_research/funnel/filter.py +73 -0
  70. bad_research-0.1.0/src/bad_research/funnel/orchestrator.py +107 -0
  71. bad_research-0.1.0/src/bad_research/funnel/rank.py +103 -0
  72. bad_research-0.1.0/src/bad_research/funnel/read.py +128 -0
  73. bad_research-0.1.0/src/bad_research/funnel/store.py +93 -0
  74. bad_research-0.1.0/src/bad_research/graph/__init__.py +1 -0
  75. bad_research-0.1.0/src/bad_research/grounding/__init__.py +51 -0
  76. bad_research-0.1.0/src/bad_research/grounding/anchors.py +149 -0
  77. bad_research-0.1.0/src/bad_research/grounding/extract.py +62 -0
  78. bad_research-0.1.0/src/bad_research/grounding/gate.py +212 -0
  79. bad_research-0.1.0/src/bad_research/grounding/nli.py +91 -0
  80. bad_research-0.1.0/src/bad_research/grounding/render.py +129 -0
  81. bad_research-0.1.0/src/bad_research/grounding/verifier.py +376 -0
  82. bad_research-0.1.0/src/bad_research/indexgen/__init__.py +1 -0
  83. bad_research-0.1.0/src/bad_research/indexgen/generator.py +256 -0
  84. bad_research-0.1.0/src/bad_research/llm/__init__.py +19 -0
  85. bad_research-0.1.0/src/bad_research/llm/anthropic.py +167 -0
  86. bad_research-0.1.0/src/bad_research/llm/base.py +54 -0
  87. bad_research-0.1.0/src/bad_research/mcp/__init__.py +1 -0
  88. bad_research-0.1.0/src/bad_research/mcp/server.py +475 -0
  89. bad_research-0.1.0/src/bad_research/models/__init__.py +1 -0
  90. bad_research-0.1.0/src/bad_research/models/graph.py +21 -0
  91. bad_research-0.1.0/src/bad_research/models/note.py +119 -0
  92. bad_research-0.1.0/src/bad_research/models/output.py +28 -0
  93. bad_research-0.1.0/src/bad_research/models/search.py +24 -0
  94. bad_research-0.1.0/src/bad_research/pipeline.py +268 -0
  95. bad_research-0.1.0/src/bad_research/providers.py +127 -0
  96. bad_research-0.1.0/src/bad_research/py.typed +0 -0
  97. bad_research-0.1.0/src/bad_research/quality/__init__.py +49 -0
  98. bad_research-0.1.0/src/bad_research/quality/consistency.py +135 -0
  99. bad_research-0.1.0/src/bad_research/quality/content_filter.py +85 -0
  100. bad_research-0.1.0/src/bad_research/quality/dedup.py +78 -0
  101. bad_research-0.1.0/src/bad_research/quality/grader.py +118 -0
  102. bad_research-0.1.0/src/bad_research/quality/injection.py +41 -0
  103. bad_research-0.1.0/src/bad_research/quality/prefilter.py +268 -0
  104. bad_research-0.1.0/src/bad_research/quality/rank.py +20 -0
  105. bad_research-0.1.0/src/bad_research/quality/recitation.py +148 -0
  106. bad_research-0.1.0/src/bad_research/quality/relevance.py +57 -0
  107. bad_research-0.1.0/src/bad_research/quality/sources.py +56 -0
  108. bad_research-0.1.0/src/bad_research/retrieval/__init__.py +12 -0
  109. bad_research-0.1.0/src/bad_research/retrieval/anchors.py +39 -0
  110. bad_research-0.1.0/src/bad_research/retrieval/base.py +28 -0
  111. bad_research-0.1.0/src/bad_research/retrieval/cache.py +194 -0
  112. bad_research-0.1.0/src/bad_research/retrieval/chunker.py +101 -0
  113. bad_research-0.1.0/src/bad_research/retrieval/chunker_code.py +221 -0
  114. bad_research-0.1.0/src/bad_research/retrieval/constants.py +66 -0
  115. bad_research-0.1.0/src/bad_research/retrieval/engine.py +318 -0
  116. bad_research-0.1.0/src/bad_research/retrieval/fts_chunks.py +52 -0
  117. bad_research-0.1.0/src/bad_research/retrieval/fusion.py +70 -0
  118. bad_research-0.1.0/src/bad_research/retrieval/reflections.py +217 -0
  119. bad_research-0.1.0/src/bad_research/retrieval/rerank.py +247 -0
  120. bad_research-0.1.0/src/bad_research/retrieval/store.py +134 -0
  121. bad_research-0.1.0/src/bad_research/search/__init__.py +1 -0
  122. bad_research-0.1.0/src/bad_research/search/filters.py +105 -0
  123. bad_research-0.1.0/src/bad_research/search/fts.py +142 -0
  124. bad_research-0.1.0/src/bad_research/serve/__init__.py +1 -0
  125. bad_research-0.1.0/src/bad_research/serve/renderer.py +124 -0
  126. bad_research-0.1.0/src/bad_research/serve/server.py +588 -0
  127. bad_research-0.1.0/src/bad_research/skills/__init__.py +1 -0
  128. bad_research-0.1.0/src/bad_research/skills/bad-research-0.5-clarify.md +81 -0
  129. bad_research-0.1.0/src/bad_research/skills/bad-research-1-decompose.md +194 -0
  130. bad_research-0.1.0/src/bad_research/skills/bad-research-1.6-plan-gate.md +113 -0
  131. bad_research-0.1.0/src/bad_research/skills/bad-research-10-triple-draft.md +285 -0
  132. bad_research-0.1.0/src/bad_research/skills/bad-research-11-synthesize.md +339 -0
  133. bad_research-0.1.0/src/bad_research/skills/bad-research-11.5-citation-verifier.md +86 -0
  134. bad_research-0.1.0/src/bad_research/skills/bad-research-12-critics.md +128 -0
  135. bad_research-0.1.0/src/bad_research/skills/bad-research-12.5-grader.md +154 -0
  136. bad_research-0.1.0/src/bad_research/skills/bad-research-13-gap-fetch.md +99 -0
  137. bad_research-0.1.0/src/bad_research/skills/bad-research-14-patcher.md +182 -0
  138. bad_research-0.1.0/src/bad_research/skills/bad-research-15-polish.md +159 -0
  139. bad_research-0.1.0/src/bad_research/skills/bad-research-16-readability-audit.md +248 -0
  140. bad_research-0.1.0/src/bad_research/skills/bad-research-2-width-sweep.md +441 -0
  141. bad_research-0.1.0/src/bad_research/skills/bad-research-3-contradiction-graph.md +73 -0
  142. bad_research-0.1.0/src/bad_research/skills/bad-research-4-loci-analysis.md +128 -0
  143. bad_research-0.1.0/src/bad_research/skills/bad-research-5-depth-investigation.md +152 -0
  144. bad_research-0.1.0/src/bad_research/skills/bad-research-6-cross-locus-reconcile.md +81 -0
  145. bad_research-0.1.0/src/bad_research/skills/bad-research-7-source-tensions.md +100 -0
  146. bad_research-0.1.0/src/bad_research/skills/bad-research-8-corpus-critic.md +134 -0
  147. bad_research-0.1.0/src/bad_research/skills/bad-research-9-evidence-digest.md +82 -0
  148. bad_research-0.1.0/src/bad_research/skills/bad-research-agentic-fast.md +86 -0
  149. bad_research-0.1.0/src/bad_research/skills/bad-research-fresh-review.md +83 -0
  150. bad_research-0.1.0/src/bad_research/skills/bad-research-query-router.md +89 -0
  151. bad_research-0.1.0/src/bad_research/skills/bad-research.md +366 -0
  152. bad_research-0.1.0/src/bad_research/skills/router.py +406 -0
  153. bad_research-0.1.0/src/bad_research/skills/routing_constants.py +193 -0
  154. bad_research-0.1.0/src/bad_research/web/__init__.py +1 -0
  155. bad_research-0.1.0/src/bad_research/web/base.py +258 -0
  156. bad_research-0.1.0/src/bad_research/web/builtin.py +127 -0
  157. bad_research-0.1.0/src/bad_research/web/content/__init__.py +55 -0
  158. bad_research-0.1.0/src/bad_research/web/content/fetch_clean.py +736 -0
  159. bad_research-0.1.0/src/bad_research/web/content/sources.py +441 -0
  160. bad_research-0.1.0/src/bad_research/web/crawl4ai_provider.py +448 -0
  161. bad_research-0.1.0/src/bad_research/web/search/__init__.py +50 -0
  162. bad_research-0.1.0/src/bad_research/web/search/base.py +249 -0
  163. bad_research-0.1.0/src/bad_research/web/search/loop.py +46 -0
  164. bad_research-0.1.0/src/bad_research/web/search/rank.py +116 -0
  165. bad_research-0.1.0/src/bad_research/web/search/rerank.py +164 -0
  166. bad_research-0.1.0/src/bad_research/web/search/route.py +48 -0
  167. bad_research-0.1.0/src/bad_research/web/search/verticals.py +393 -0
  168. bad_research-0.1.0/tests/__init__.py +1 -0
  169. bad_research-0.1.0/tests/conftest.py +39 -0
  170. bad_research-0.1.0/tests/test_browse/__init__.py +0 -0
  171. bad_research-0.1.0/tests/test_browse/conftest.py +153 -0
  172. bad_research-0.1.0/tests/test_browse/test_agent_browser_browse.py +155 -0
  173. bad_research-0.1.0/tests/test_browse/test_agent_browser_cli.py +101 -0
  174. bad_research-0.1.0/tests/test_browse/test_agent_browser_snapshot.py +80 -0
  175. bad_research-0.1.0/tests/test_browse/test_aql.py +104 -0
  176. bad_research-0.1.0/tests/test_browse/test_aql_resolver.py +83 -0
  177. bad_research-0.1.0/tests/test_browse/test_base.py +56 -0
  178. bad_research-0.1.0/tests/test_browse/test_cache.py +40 -0
  179. bad_research-0.1.0/tests/test_browse/test_extract_llm.py +81 -0
  180. bad_research-0.1.0/tests/test_browse/test_fetcher_hook.py +75 -0
  181. bad_research-0.1.0/tests/test_browse/test_graceful_degradation.py +63 -0
  182. bad_research-0.1.0/tests/test_browse/test_ladder.py +164 -0
  183. bad_research-0.1.0/tests/test_calibrate/__init__.py +0 -0
  184. bad_research-0.1.0/tests/test_calibrate/conftest.py +64 -0
  185. bad_research-0.1.0/tests/test_calibrate/test_baselines.py +50 -0
  186. bad_research-0.1.0/tests/test_calibrate/test_calibrate_cmd.py +58 -0
  187. bad_research-0.1.0/tests/test_calibrate/test_cost.py +75 -0
  188. bad_research-0.1.0/tests/test_calibrate/test_gate_cmd.py +85 -0
  189. bad_research-0.1.0/tests/test_calibrate/test_golden.py +114 -0
  190. bad_research-0.1.0/tests/test_calibrate/test_harness.py +107 -0
  191. bad_research-0.1.0/tests/test_calibrate/test_judge.py +79 -0
  192. bad_research-0.1.0/tests/test_calibrate/test_judge_rails.py +104 -0
  193. bad_research-0.1.0/tests/test_calibrate/test_runner_bridge.py +20 -0
  194. bad_research-0.1.0/tests/test_cli/__init__.py +0 -0
  195. bad_research-0.1.0/tests/test_cli/test_cli_subcommands.py +244 -0
  196. bad_research-0.1.0/tests/test_cli/test_keyless_rewire.py +431 -0
  197. bad_research-0.1.0/tests/test_config/__init__.py +1 -0
  198. bad_research-0.1.0/tests/test_config/test_config.py +80 -0
  199. bad_research-0.1.0/tests/test_content/__init__.py +0 -0
  200. bad_research-0.1.0/tests/test_content/conftest.py +88 -0
  201. bad_research-0.1.0/tests/test_content/test_cache_fetch.py +211 -0
  202. bad_research-0.1.0/tests/test_content/test_classify_source.py +30 -0
  203. bad_research-0.1.0/tests/test_content/test_highlights.py +28 -0
  204. bad_research-0.1.0/tests/test_content/test_init_shadow.py +30 -0
  205. bad_research-0.1.0/tests/test_content/test_keyless_guard.py +78 -0
  206. bad_research-0.1.0/tests/test_content/test_llm_clean.py +43 -0
  207. bad_research-0.1.0/tests/test_content/test_main_content.py +39 -0
  208. bad_research-0.1.0/tests/test_content/test_markdown.py +32 -0
  209. bad_research-0.1.0/tests/test_content/test_metadata.py +48 -0
  210. bad_research-0.1.0/tests/test_content/test_pdf.py +18 -0
  211. bad_research-0.1.0/tests/test_content/test_render_ssrf.py +130 -0
  212. bad_research-0.1.0/tests/test_content/test_sources_cli.py +87 -0
  213. bad_research-0.1.0/tests/test_content/test_sources_html.py +96 -0
  214. bad_research-0.1.0/tests/test_content/test_sources_ssrf.py +64 -0
  215. bad_research-0.1.0/tests/test_content/test_strip_boilerplate.py +53 -0
  216. bad_research-0.1.0/tests/test_core/__init__.py +0 -0
  217. bad_research-0.1.0/tests/test_core/test_ssrf.py +231 -0
  218. bad_research-0.1.0/tests/test_core/test_ssrf_predicate.py +60 -0
  219. bad_research-0.1.0/tests/test_embed/__init__.py +1 -0
  220. bad_research-0.1.0/tests/test_embed/test_base.py +51 -0
  221. bad_research-0.1.0/tests/test_embed/test_bge_local.py +23 -0
  222. bad_research-0.1.0/tests/test_funnel/__init__.py +0 -0
  223. bad_research-0.1.0/tests/test_funnel/conftest.py +194 -0
  224. bad_research-0.1.0/tests/test_funnel/test_async.py +52 -0
  225. bad_research-0.1.0/tests/test_funnel/test_canonical.py +39 -0
  226. bad_research-0.1.0/tests/test_funnel/test_config.py +48 -0
  227. bad_research-0.1.0/tests/test_funnel/test_dedup.py +54 -0
  228. bad_research-0.1.0/tests/test_funnel/test_fanout.py +59 -0
  229. bad_research-0.1.0/tests/test_funnel/test_filter.py +66 -0
  230. bad_research-0.1.0/tests/test_funnel/test_orchestrator.py +117 -0
  231. bad_research-0.1.0/tests/test_funnel/test_rank.py +58 -0
  232. bad_research-0.1.0/tests/test_funnel/test_read.py +99 -0
  233. bad_research-0.1.0/tests/test_grounding/__init__.py +0 -0
  234. bad_research-0.1.0/tests/test_grounding/conftest.py +38 -0
  235. bad_research-0.1.0/tests/test_grounding/test_anchors.py +135 -0
  236. bad_research-0.1.0/tests/test_grounding/test_confidence_band.py +31 -0
  237. bad_research-0.1.0/tests/test_grounding/test_extract.py +37 -0
  238. bad_research-0.1.0/tests/test_grounding/test_gate.py +163 -0
  239. bad_research-0.1.0/tests/test_grounding/test_host_judge.py +175 -0
  240. bad_research-0.1.0/tests/test_grounding/test_nli.py +114 -0
  241. bad_research-0.1.0/tests/test_grounding/test_public_api.py +13 -0
  242. bad_research-0.1.0/tests/test_grounding/test_render.py +120 -0
  243. bad_research-0.1.0/tests/test_grounding/test_seam.py +11 -0
  244. bad_research-0.1.0/tests/test_grounding/test_verifier.py +409 -0
  245. bad_research-0.1.0/tests/test_install/__init__.py +0 -0
  246. bad_research-0.1.0/tests/test_install/test_install_cli_e2e.py +34 -0
  247. bad_research-0.1.0/tests/test_install/test_install_global.py +51 -0
  248. bad_research-0.1.0/tests/test_install/test_install_project.py +32 -0
  249. bad_research-0.1.0/tests/test_install/test_step_list.py +25 -0
  250. bad_research-0.1.0/tests/test_llm/__init__.py +1 -0
  251. bad_research-0.1.0/tests/test_llm/test_anthropic.py +289 -0
  252. bad_research-0.1.0/tests/test_llm/test_base.py +52 -0
  253. bad_research-0.1.0/tests/test_mcp/__init__.py +0 -0
  254. bad_research-0.1.0/tests/test_mcp/test_mcp_tools.py +16 -0
  255. bad_research-0.1.0/tests/test_packaging/__init__.py +0 -0
  256. bad_research-0.1.0/tests/test_packaging/test_base_leanness.py +88 -0
  257. bad_research-0.1.0/tests/test_packaging/test_doctor.py +63 -0
  258. bad_research-0.1.0/tests/test_packaging/test_install_idempotent.py +51 -0
  259. bad_research-0.1.0/tests/test_packaging/test_live_skip.py +18 -0
  260. bad_research-0.1.0/tests/test_packaging/test_pyproject.py +104 -0
  261. bad_research-0.1.0/tests/test_packaging/test_pytest_config.py +65 -0
  262. bad_research-0.1.0/tests/test_packaging/test_wheel_build.py +80 -0
  263. bad_research-0.1.0/tests/test_pipeline/__init__.py +0 -0
  264. bad_research-0.1.0/tests/test_pipeline/test_build_real.py +98 -0
  265. bad_research-0.1.0/tests/test_pipeline/test_run_query.py +134 -0
  266. bad_research-0.1.0/tests/test_providers.py +83 -0
  267. bad_research-0.1.0/tests/test_quality/__init__.py +0 -0
  268. bad_research-0.1.0/tests/test_quality/conftest.py +104 -0
  269. bad_research-0.1.0/tests/test_quality/test_consistency.py +132 -0
  270. bad_research-0.1.0/tests/test_quality/test_content_filter.py +64 -0
  271. bad_research-0.1.0/tests/test_quality/test_dedup.py +48 -0
  272. bad_research-0.1.0/tests/test_quality/test_grader.py +94 -0
  273. bad_research-0.1.0/tests/test_quality/test_injection.py +40 -0
  274. bad_research-0.1.0/tests/test_quality/test_pipeline_e2e.py +66 -0
  275. bad_research-0.1.0/tests/test_quality/test_prefilter.py +180 -0
  276. bad_research-0.1.0/tests/test_quality/test_public_api.py +33 -0
  277. bad_research-0.1.0/tests/test_quality/test_rank.py +56 -0
  278. bad_research-0.1.0/tests/test_quality/test_recitation.py +112 -0
  279. bad_research-0.1.0/tests/test_quality/test_relevance.py +65 -0
  280. bad_research-0.1.0/tests/test_quality/test_sources.py +55 -0
  281. bad_research-0.1.0/tests/test_retrieval/__init__.py +0 -0
  282. bad_research-0.1.0/tests/test_retrieval/conftest.py +104 -0
  283. bad_research-0.1.0/tests/test_retrieval/test_anchors.py +35 -0
  284. bad_research-0.1.0/tests/test_retrieval/test_base.py +24 -0
  285. bad_research-0.1.0/tests/test_retrieval/test_cache.py +91 -0
  286. bad_research-0.1.0/tests/test_retrieval/test_chunker.py +55 -0
  287. bad_research-0.1.0/tests/test_retrieval/test_chunker_code.py +104 -0
  288. bad_research-0.1.0/tests/test_retrieval/test_config.py +15 -0
  289. bad_research-0.1.0/tests/test_retrieval/test_constants.py +42 -0
  290. bad_research-0.1.0/tests/test_retrieval/test_engine.py +280 -0
  291. bad_research-0.1.0/tests/test_retrieval/test_fts_chunks.py +46 -0
  292. bad_research-0.1.0/tests/test_retrieval/test_fusion.py +93 -0
  293. bad_research-0.1.0/tests/test_retrieval/test_import_keyless.py +60 -0
  294. bad_research-0.1.0/tests/test_retrieval/test_no_keys_invariant.py +43 -0
  295. bad_research-0.1.0/tests/test_retrieval/test_reflections.py +150 -0
  296. bad_research-0.1.0/tests/test_retrieval/test_rerank.py +225 -0
  297. bad_research-0.1.0/tests/test_retrieval/test_schema_migration.py +32 -0
  298. bad_research-0.1.0/tests/test_skills/__init__.py +0 -0
  299. bad_research-0.1.0/tests/test_skills/conftest.py +15 -0
  300. bad_research-0.1.0/tests/test_skills/test_agentic_fast_skill.py +16 -0
  301. bad_research-0.1.0/tests/test_skills/test_all_skills_valid.py +15 -0
  302. bad_research-0.1.0/tests/test_skills/test_citation_verifier_skill.py +16 -0
  303. bad_research-0.1.0/tests/test_skills/test_clarify_skill.py +15 -0
  304. bad_research-0.1.0/tests/test_skills/test_delegation_contract.py +46 -0
  305. bad_research-0.1.0/tests/test_skills/test_entry_skill.py +22 -0
  306. bad_research-0.1.0/tests/test_skills/test_fresh_review_skill.py +15 -0
  307. bad_research-0.1.0/tests/test_skills/test_grader_skill.py +27 -0
  308. bad_research-0.1.0/tests/test_skills/test_light_critic.py +101 -0
  309. bad_research-0.1.0/tests/test_skills/test_modified_stages.py +189 -0
  310. bad_research-0.1.0/tests/test_skills/test_plan_gate.py +162 -0
  311. bad_research-0.1.0/tests/test_skills/test_router.py +74 -0
  312. bad_research-0.1.0/tests/test_skills/test_router_effort.py +114 -0
  313. bad_research-0.1.0/tests/test_skills/test_router_modality.py +169 -0
  314. bad_research-0.1.0/tests/test_skills/test_router_shape.py +244 -0
  315. bad_research-0.1.0/tests/test_skills/test_router_skill.py +15 -0
  316. bad_research-0.1.0/tests/test_skills/test_router_tiering.py +195 -0
  317. bad_research-0.1.0/tests/test_skills/test_validate_self.py +18 -0
  318. bad_research-0.1.0/tests/test_skills/validate.py +54 -0
  319. bad_research-0.1.0/tests/test_web/__init__.py +0 -0
  320. bad_research-0.1.0/tests/test_web/fixtures/arxiv_query.atom +23 -0
  321. bad_research-0.1.0/tests/test_web/fixtures/crossref_works.json +16 -0
  322. bad_research-0.1.0/tests/test_web/fixtures/europepmc_search.json +20 -0
  323. bad_research-0.1.0/tests/test_web/fixtures/openalex_works.json +16 -0
  324. bad_research-0.1.0/tests/test_web/fixtures/pubmed_esearch.json +7 -0
  325. bad_research-0.1.0/tests/test_web/fixtures/pubmed_esummary.json +7 -0
  326. bad_research-0.1.0/tests/test_web/fixtures/s2_search.json +16 -0
  327. bad_research-0.1.0/tests/test_web/fixtures/wikipedia_search.json +1 -0
  328. bad_research-0.1.0/tests/test_web/fixtures/wikipedia_summary.json +1 -0
  329. bad_research-0.1.0/tests/test_web/test_provider_factory.py +71 -0
  330. bad_research-0.1.0/tests/test_web/test_search_live.py +49 -0
  331. bad_research-0.1.0/tests/test_web/test_search_loop.py +75 -0
  332. bad_research-0.1.0/tests/test_web/test_search_providers.py +150 -0
  333. bad_research-0.1.0/tests/test_web/test_search_rank.py +73 -0
  334. bad_research-0.1.0/tests/test_web/test_search_rerank.py +122 -0
  335. bad_research-0.1.0/tests/test_web/test_search_route.py +45 -0
  336. bad_research-0.1.0/tests/test_web/test_search_verticals.py +146 -0
  337. bad_research-0.1.0/tests/test_web/test_web_search_query.py +78 -0
  338. bad_research-0.1.0/uv.lock +3936 -0
@@ -0,0 +1,25 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ venv/
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ .pytest_cache/
9
+ .mypy_cache/
10
+ .ruff_cache/
11
+ .coverage
12
+ htmlcov/
13
+ .DS_Store
14
+
15
+ # dev-only build artifacts — not shipped (kept on disk for dev, out of the prod repo)
16
+ docs/plans/
17
+ docs/investigation/
18
+ docs/INTERFACES.md
19
+ docs/INTERFACES_KEYLESS.md
20
+ docs/KEYLESS_REBUILD_PLAN_OUTLINE.md
21
+ docs/SPEC.md
22
+ docs/enhancements/
23
+
24
+ # research vault — per-run output/scratch (eval + real runs), never shipped
25
+ research/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bad Research
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,143 @@
1
+ Metadata-Version: 2.4
2
+ Name: bad-research
3
+ Version: 0.1.0
4
+ Summary: michael jackson bad
5
+ Project-URL: Homepage, https://github.com/LeventySeven/badresearch
6
+ Project-URL: Repository, https://github.com/LeventySeven/badresearch
7
+ Project-URL: Issues, https://github.com/LeventySeven/badresearch/issues
8
+ Author: Bad Research
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: agent,claude,cli,deep-research,llm,rag,research,retrieval
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Requires-Python: <3.14,>=3.11
20
+ Requires-Dist: anthropic>=0.40
21
+ Requires-Dist: beautifulsoup4>=4.12
22
+ Requires-Dist: crawl4ai>=0.4
23
+ Requires-Dist: dateparser>=1.2
24
+ Requires-Dist: ddgs>=9.14
25
+ Requires-Dist: feedparser>=6.0
26
+ Requires-Dist: httpx>=0.27
27
+ Requires-Dist: jinja2>=3.1
28
+ Requires-Dist: langdetect>=1.0.9
29
+ Requires-Dist: lxml>=5.0
30
+ Requires-Dist: platformdirs>=4.0
31
+ Requires-Dist: pydantic>=2.0
32
+ Requires-Dist: pymupdf4llm>=0.0.17
33
+ Requires-Dist: pymupdf>=1.24
34
+ Requires-Dist: pyyaml>=6.0
35
+ Requires-Dist: rank-bm25>=0.2
36
+ Requires-Dist: rapidfuzz>=3.0
37
+ Requires-Dist: rich>=13.0
38
+ Requires-Dist: snowballstemmer>=2.2
39
+ Requires-Dist: trafilatura>=1.8
40
+ Requires-Dist: tree-sitter-language-pack>=0.7
41
+ Requires-Dist: tree-sitter>=0.23
42
+ Requires-Dist: typer>=0.9.0
43
+ Provides-Extra: all
44
+ Requires-Dist: lancedb>=0.13; extra == 'all'
45
+ Requires-Dist: mcp>=1.6; extra == 'all'
46
+ Requires-Dist: playwright>=1.40; extra == 'all'
47
+ Requires-Dist: pyarrow>=15.0; extra == 'all'
48
+ Requires-Dist: sentence-transformers>=3.0; extra == 'all'
49
+ Requires-Dist: torch>=2.0; extra == 'all'
50
+ Provides-Extra: browse
51
+ Requires-Dist: playwright>=1.40; extra == 'browse'
52
+ Provides-Extra: dev
53
+ Requires-Dist: mcp>=1.6; extra == 'dev'
54
+ Requires-Dist: mypy>=1.8; extra == 'dev'
55
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
56
+ Requires-Dist: pytest-cov>=4.1; extra == 'dev'
57
+ Requires-Dist: pytest>=7.4; extra == 'dev'
58
+ Requires-Dist: respx>=0.21; extra == 'dev'
59
+ Requires-Dist: ruff>=0.3; extra == 'dev'
60
+ Provides-Extra: local
61
+ Requires-Dist: lancedb>=0.13; extra == 'local'
62
+ Requires-Dist: pyarrow>=15.0; extra == 'local'
63
+ Requires-Dist: sentence-transformers>=3.0; extra == 'local'
64
+ Requires-Dist: torch>=2.0; extra == 'local'
65
+ Provides-Extra: mcp
66
+ Requires-Dist: mcp>=1.6; extra == 'mcp'
67
+ Description-Content-Type: text/markdown
68
+
69
+ <p align="center">
70
+ <img src="assets/banner.png" alt="BAD — michael jackson bad" width="520">
71
+ </p>
72
+
73
+ <h1 align="center">Bad Research</h1>
74
+
75
+ <p align="center"><em>michael jackson bad</em></p>
76
+
77
+ A **keyless** deep-research agent that runs as a Claude Code skill — a
78
+ fork-and-enhance of [hyperresearch](https://github.com/jordan-gibbs/hyperresearch).
79
+ It searches wide, filters garbage, grounds every claim to a source, and needs
80
+ **zero API keys**: the Claude Code host model supplies all inference, exactly like
81
+ hyperresearch. Optional local CLIs and a `[local]` neural extra are enhancements,
82
+ never requirements.
83
+
84
+ ## Install
85
+
86
+ Bad Research is a small CLI that registers itself as a Claude Code skill. No API keys. Requires Python 3.11–3.13.
87
+
88
+ ```bash
89
+ # Install the CLI (pipx or uv — either works)
90
+ pipx install bad-research
91
+ uv tool install bad-research
92
+
93
+ # Register the /bad-research skill into ~/.claude
94
+ bad install
95
+
96
+ # Verify
97
+ bad doctor
98
+ ```
99
+
100
+ `bad install` writes the entry skill to `~/.claude/skills/bad-research/`; the per-step
101
+ skills install lazily on first use. For a project-local install instead of global, run
102
+ `bad install --project` inside the project. `bad doctor` shows what's wired (host model,
103
+ keyless search/browse, the optional external CLIs it can drive, the `[local]` neural stack).
104
+
105
+ ## Use it in Claude Code
106
+
107
+ After `bad install`, open Claude Code in any project and either:
108
+
109
+ - **Invoke it directly** — type the slash command with your question:
110
+ ```
111
+ /bad-research Is open-source AI more dangerous than closed-source for national security?
112
+ ```
113
+ - **Let Claude trigger it** — just ask a research-shaped question (*"write me a cited report
114
+ comparing vector databases"*, *"literature review on GLP-1 drugs"*) and Claude loads the
115
+ skill automatically.
116
+
117
+ It scales to the question: a simple lookup gets a fast cited answer in minutes; a broad or
118
+ contested one runs the full adversarially-reviewed pipeline (~1.5–2.5 h). The final report
119
+ and every fetched source land in a vault under `./research/` that compounds across sessions.
120
+
121
+ > Want the latest unreleased build? Install from source: `pipx install git+https://github.com/LeventySeven/badresearch.git`
122
+
123
+ ## What it does
124
+
125
+ A tier-adaptive pipeline turns a question into an audited, fully-cited report, and
126
+ every fetched source lands in a persistent, searchable vault that compounds across
127
+ sessions. Keyless by design:
128
+
129
+ - **Search** — the host `WebSearch` tool + DuckDuckGo + 7 scholarly APIs, fused and reranked by the host model.
130
+ - **Content** — a native fetch-and-clean pipeline (readability → markdown → optional LLM clean), SSRF-guarded.
131
+ - **Browse** — an agentic observe → act → extract loop driven by a local, keyless headless browser.
132
+ - **Retrieve** — SQLite FTS5/BM25 by default (no model required), with an optional local neural lane.
133
+ - **Ground** — every report sentence is checked against its source; uncited claims are blocked.
134
+
135
+ ## How it works & where the patterns came from
136
+
137
+ Bad Research takes hyperresearch as its base and enhances each stage with patterns
138
+ drawn from the best deep-research systems — Perplexity, Gemini, Firecrawl, Stagehand,
139
+ AgentQL, and others — reimplemented to run **keyless** on the host model. The full
140
+ write-up, stage by stage with provenance, is in
141
+ [**docs/HOW_IT_WORKS.md**](docs/HOW_IT_WORKS.md).
142
+
143
+ MIT licensed.
@@ -0,0 +1,75 @@
1
+ <p align="center">
2
+ <img src="assets/banner.png" alt="BAD — michael jackson bad" width="520">
3
+ </p>
4
+
5
+ <h1 align="center">Bad Research</h1>
6
+
7
+ <p align="center"><em>michael jackson bad</em></p>
8
+
9
+ A **keyless** deep-research agent that runs as a Claude Code skill — a
10
+ fork-and-enhance of [hyperresearch](https://github.com/jordan-gibbs/hyperresearch).
11
+ It searches wide, filters garbage, grounds every claim to a source, and needs
12
+ **zero API keys**: the Claude Code host model supplies all inference, exactly like
13
+ hyperresearch. Optional local CLIs and a `[local]` neural extra are enhancements,
14
+ never requirements.
15
+
16
+ ## Install
17
+
18
+ Bad Research is a small CLI that registers itself as a Claude Code skill. No API keys. Requires Python 3.11–3.13.
19
+
20
+ ```bash
21
+ # Install the CLI (pipx or uv — either works)
22
+ pipx install bad-research
23
+ uv tool install bad-research
24
+
25
+ # Register the /bad-research skill into ~/.claude
26
+ bad install
27
+
28
+ # Verify
29
+ bad doctor
30
+ ```
31
+
32
+ `bad install` writes the entry skill to `~/.claude/skills/bad-research/`; the per-step
33
+ skills install lazily on first use. For a project-local install instead of global, run
34
+ `bad install --project` inside the project. `bad doctor` shows what's wired (host model,
35
+ keyless search/browse, the optional external CLIs it can drive, the `[local]` neural stack).
36
+
37
+ ## Use it in Claude Code
38
+
39
+ After `bad install`, open Claude Code in any project and either:
40
+
41
+ - **Invoke it directly** — type the slash command with your question:
42
+ ```
43
+ /bad-research Is open-source AI more dangerous than closed-source for national security?
44
+ ```
45
+ - **Let Claude trigger it** — just ask a research-shaped question (*"write me a cited report
46
+ comparing vector databases"*, *"literature review on GLP-1 drugs"*) and Claude loads the
47
+ skill automatically.
48
+
49
+ It scales to the question: a simple lookup gets a fast cited answer in minutes; a broad or
50
+ contested one runs the full adversarially-reviewed pipeline (~1.5–2.5 h). The final report
51
+ and every fetched source land in a vault under `./research/` that compounds across sessions.
52
+
53
+ > Want the latest unreleased build? Install from source: `pipx install git+https://github.com/LeventySeven/badresearch.git`
54
+
55
+ ## What it does
56
+
57
+ A tier-adaptive pipeline turns a question into an audited, fully-cited report, and
58
+ every fetched source lands in a persistent, searchable vault that compounds across
59
+ sessions. Keyless by design:
60
+
61
+ - **Search** — the host `WebSearch` tool + DuckDuckGo + 7 scholarly APIs, fused and reranked by the host model.
62
+ - **Content** — a native fetch-and-clean pipeline (readability → markdown → optional LLM clean), SSRF-guarded.
63
+ - **Browse** — an agentic observe → act → extract loop driven by a local, keyless headless browser.
64
+ - **Retrieve** — SQLite FTS5/BM25 by default (no model required), with an optional local neural lane.
65
+ - **Ground** — every report sentence is checked against its source; uncited claims are blocked.
66
+
67
+ ## How it works & where the patterns came from
68
+
69
+ Bad Research takes hyperresearch as its base and enhances each stage with patterns
70
+ drawn from the best deep-research systems — Perplexity, Gemini, Firecrawl, Stagehand,
71
+ AgentQL, and others — reimplemented to run **keyless** on the host model. The full
72
+ write-up, stage by stage with provenance, is in
73
+ [**docs/HOW_IT_WORKS.md**](docs/HOW_IT_WORKS.md).
74
+
75
+ MIT licensed.
Binary file
@@ -0,0 +1,142 @@
1
+ # How Bad Research was built — and where the patterns came from
2
+
3
+ Bad Research is a **fork-and-enhance of [hyperresearch](https://github.com/jordan-gibbs/hyperresearch)**.
4
+ Hyperresearch gave us the foundation: a tier-adaptive, ~16-stage research pipeline
5
+ driven as a Claude Code skill, with a persistent markdown + SQLite vault that
6
+ compounds knowledge across sessions. We kept that whole spine and enhanced each
7
+ stage with the best pattern we could find for it — the approaches that the leading
8
+ deep-research and web-agent systems are known for — and reimplemented every one of
9
+ them to run **keyless**, on the Claude Code host model, with no third-party API key.
10
+
11
+ This document is the honest tour: each stage, the pattern it borrows, and who
12
+ pioneered that pattern. Nothing here needs an API key; where a pattern was
13
+ originally a paid product, we adopted the *idea* and rebuilt it on the host model
14
+ and open tooling.
15
+
16
+ ---
17
+
18
+ ## The build approach
19
+
20
+ 1. **Start from hyperresearch.** Its pipeline, vault, skill packaging, and grounding
21
+ gate are the base. We did not rewrite what already worked.
22
+ 2. **Enhance stage by stage.** For each stage — search, content, browse, retrieval,
23
+ reranking, grounding, the control loop — we took the strongest known pattern and
24
+ wired it in behind a clean seam.
25
+ 3. **Keyless by design.** Every enhancement is implemented on the host model (via a
26
+ single `LLMProvider` seam) plus open-source libraries and optional local CLIs.
27
+ No vendor key is ever required to install or run; `bad doctor` proves it.
28
+ 4. **Built with reviews.** Each stage was implemented and then independently
29
+ reviewed for correctness, security (e.g. SSRF), and faithfulness to the pattern
30
+ before it landed.
31
+
32
+ ---
33
+
34
+ ## The stages and their provenance
35
+
36
+ ### Search — wide recall, then a relevance-gated loop
37
+ *Pattern from: Perplexity.* Perplexity's deep search popularised the loop of
38
+ casting a wide net across many sources and then **re-querying until the results are
39
+ actually good enough** rather than answering from the first page. We implement that
40
+ as a "retrieve-until-good" loop: a relevance gate (default 0.70) and a minimum
41
+ pass-fraction (0.30) decide whether to expand and search again, up to a small round
42
+ cap. Recall comes from the host `WebSearch` tool + DuckDuckGo (`ddgs`), with an
43
+ optional self-hosted SearXNG if you have one.
44
+
45
+ ### Scholarly verticals — go to the primary sources
46
+ *Pattern from: the open scholarly ecosystem.* For research-grade questions, general
47
+ web search isn't enough, so we route to the primary academic APIs directly —
48
+ **arXiv, OpenAlex, Crossref, Semantic Scholar, Europe PMC, PubMed, and Wikipedia**.
49
+ All are free and keyless; an intent classifier sends a query to the right ones
50
+ (medical → PubMed/Europe PMC, academic → OpenAlex/arXiv, etc.).
51
+
52
+ ### Rank fusion — merge many ranked lists fairly
53
+ *Pattern from: Reciprocal Rank Fusion (a standard IR technique, used by systems like
54
+ Exa).* When several sources each return their own ranked list, we combine them with
55
+ **RRF (k = 60)** so no single source dominates and consensus results rise to the top.
56
+
57
+ ### Reranking — a cross-encoder pass for precision
58
+ *Pattern from: Cohere Rerank.* Cohere popularised dropping a cross-encoder reranker
59
+ in front of the final results to sharply improve precision. We adopt the pattern but
60
+ keep it **keyless**: the **host model itself** scores each candidate against the
61
+ query with one frozen rubric prompt. It's a frontier cross-encoder you already have —
62
+ ≥ rerank-API quality at zero dollars. (An optional `[local]` extra adds an offline
63
+ `ms-marco-MiniLM` cross-encoder for users who want it.)
64
+
65
+ ### Content extraction — clean signal out of messy HTML
66
+ *Pattern from: Firecrawl.* Firecrawl is known for turning arbitrary pages into clean,
67
+ model-ready markdown by stripping boilerplate (nav, ads, cookie banners) before
68
+ conversion. We rebuilt that natively: a readability/pruning pass strips chrome,
69
+ HTML→markdown conversion preserves citations and structure, PDFs go through a PDF
70
+ text extractor, and a final optional LLM-clean pass tidies what's left — all with a
71
+ strict anti-prompt-injection preamble so page content is always treated as data.
72
+ Every fetch is **SSRF-guarded** (private-IP/metadata-endpoint denylist, re-validated
73
+ on each redirect).
74
+
75
+ ### Agentic browse — observe, act, extract
76
+ *Pattern from: Stagehand / Browserbase.* The modern web-agent pattern is a loop of
77
+ **observe** the page's accessibility tree → **act** (click/type/navigate) → **extract**
78
+ structured data, with the model choosing actions against stable element references.
79
+ We use Stagehand's well-known observe/act/extract prompts as the loop's brain — but
80
+ instead of a paid cloud browser, we drive **[vercel's `agent-browser`](https://github.com/vercel-labs/agent-browser)**,
81
+ a local, keyless headless-Chrome CLI (with `lightpanda` as a fast optional engine).
82
+ The model only ever acts on element references that exist in the live page snapshot,
83
+ so it can't be steered onto a hallucinated element.
84
+
85
+ ### Element querying — ask the page in a query language
86
+ *Pattern from: AgentQL.* AgentQL's idea is a small declarative query language for
87
+ locating page elements by role/intent rather than brittle CSS selectors. We ported a
88
+ parser for that query style so the browse layer can resolve elements the same way —
89
+ again, purely local, no service.
90
+
91
+ ### Retrieval — hybrid lexical + (optional) semantic
92
+ *Pattern from: Perplexity-style hybrid retrieval.* The robust pattern is to blend
93
+ keyword and semantic recall rather than rely on either alone. Our **default is
94
+ keyless and model-free**: SQLite **FTS5/BM25** lexical recall, three-tier rank
95
+ fusion, and a lexical semantic cache (0.85 overlap) — fast, deterministic, and it
96
+ runs anywhere. If you install the `[local]` extra, a dense vector lane (a local
97
+ `bge` embedder + **LanceDB** ANN, the pattern LanceDB is built for) is used
98
+ automatically on large corpora, fused with BM25 via RRF.
99
+
100
+ ### Grounding & no-hallucination — cite or don't say it
101
+ *Pattern from: Gemini's grounding & recitation guarantees.* Gemini is known for
102
+ binding generated claims back to retrieved evidence and guarding against verbatim
103
+ recitation. We enforce both deterministically: every report sentence is checked
104
+ against its cited source (exact-match → local NLI → a host-model gate), **uncited
105
+ factual claims are blocked**, and a recitation gate flags any sentence that copies a
106
+ source too closely (a 12-word verbatim run or >50% overlap) — with a carve-out only
107
+ for genuine, attributed direct quotes.
108
+
109
+ ### Reasoning-effort dial — spend compute where it matters
110
+ *Pattern from: OpenAI's reasoning-effort control.* We expose a `--reasoning-effort`
111
+ continuum (minimal → low → medium → high) that maps to route, model tier, fetch
112
+ budget, and a token ceiling, with a defined degrade order so the system spends more
113
+ only when the question warrants it.
114
+
115
+ ### Confidence-band hedging — say how sure it is
116
+ *Pattern from: calibrated-uncertainty practice in research assistants.* The final
117
+ report's claims carry a confidence band derived from grounding scores, so
118
+ low-confidence statements are hedged rather than asserted flatly.
119
+
120
+ ---
121
+
122
+ ## What's keyless vs. optional
123
+
124
+ | Capability | Default (keyless, no setup) | Optional enhancement |
125
+ |---|---|---|
126
+ | Inference | Claude Code host model | — |
127
+ | Web search | host `WebSearch` + DuckDuckGo + 7 scholarly APIs | self-hosted SearXNG |
128
+ | Reranking | host-model cross-encoder | `[local]` `ms-marco-MiniLM` |
129
+ | Retrieval | SQLite FTS5/BM25 | `[local]` `bge` + LanceDB dense lane |
130
+ | Content render | native httpx + readability | `crawl4ai` JS render (bundled) |
131
+ | Browse | — | `agent-browser` / `lightpanda` CLIs |
132
+ | Media transcripts | — | `yt-dlp` CLI |
133
+
134
+ Everything in the left column works the moment you `pip install bad-research`. The
135
+ right column is detected at runtime (`bad doctor` shows the status) and degrades
136
+ gracefully when absent — it never blocks a run.
137
+
138
+ ---
139
+
140
+ *Built on the shoulders of [hyperresearch](https://github.com/jordan-gibbs/hyperresearch),
141
+ with patterns from Perplexity, Gemini, Cohere, Firecrawl, Stagehand/Browserbase,
142
+ AgentQL, LanceDB, and the open scholarly web — all reimplemented keyless.*
@@ -0,0 +1,179 @@
1
+ {
2
+ "pass_rate": 1.0,
3
+ "total": 8,
4
+ "components": {
5
+ "decompose": 1.0,
6
+ "retrieval": 1.0,
7
+ "synthesis": 1.0
8
+ },
9
+ "cases": [
10
+ {
11
+ "id": "01_causal_light",
12
+ "passed": true,
13
+ "verdict": {
14
+ "rails": {
15
+ "factual": "pass",
16
+ "citation": "pass",
17
+ "completeness": "pass",
18
+ "source_quality": "pass",
19
+ "efficiency": "pass"
20
+ },
21
+ "pass_rate": 1.0,
22
+ "passed": true,
23
+ "rationale": "deterministic offline rubric"
24
+ },
25
+ "components": {
26
+ "decompose": true,
27
+ "retrieval": true,
28
+ "synthesis": true
29
+ }
30
+ },
31
+ {
32
+ "id": "02_comparison",
33
+ "passed": true,
34
+ "verdict": {
35
+ "rails": {
36
+ "factual": "pass",
37
+ "citation": "pass",
38
+ "completeness": "pass",
39
+ "source_quality": "pass",
40
+ "efficiency": "pass"
41
+ },
42
+ "pass_rate": 1.0,
43
+ "passed": true,
44
+ "rationale": "deterministic offline rubric"
45
+ },
46
+ "components": {
47
+ "decompose": true,
48
+ "retrieval": true,
49
+ "synthesis": true
50
+ }
51
+ },
52
+ {
53
+ "id": "03_multidomain_full",
54
+ "passed": true,
55
+ "verdict": {
56
+ "rails": {
57
+ "factual": "pass",
58
+ "citation": "pass",
59
+ "completeness": "pass",
60
+ "source_quality": "pass",
61
+ "efficiency": "pass"
62
+ },
63
+ "pass_rate": 1.0,
64
+ "passed": true,
65
+ "rationale": "deterministic offline rubric"
66
+ },
67
+ "components": {
68
+ "decompose": true,
69
+ "retrieval": true,
70
+ "synthesis": true
71
+ }
72
+ },
73
+ {
74
+ "id": "04_contested_argumentative",
75
+ "passed": true,
76
+ "verdict": {
77
+ "rails": {
78
+ "factual": "pass",
79
+ "citation": "pass",
80
+ "completeness": "pass",
81
+ "source_quality": "pass",
82
+ "efficiency": "pass"
83
+ },
84
+ "pass_rate": 1.0,
85
+ "passed": true,
86
+ "rationale": "deterministic offline rubric"
87
+ },
88
+ "components": {
89
+ "decompose": true,
90
+ "retrieval": true,
91
+ "synthesis": true
92
+ }
93
+ },
94
+ {
95
+ "id": "05_definitional",
96
+ "passed": true,
97
+ "verdict": {
98
+ "rails": {
99
+ "factual": "pass",
100
+ "citation": "pass",
101
+ "completeness": "pass",
102
+ "source_quality": "pass",
103
+ "efficiency": "pass"
104
+ },
105
+ "pass_rate": 1.0,
106
+ "passed": true,
107
+ "rationale": "deterministic offline rubric"
108
+ },
109
+ "components": {
110
+ "decompose": true,
111
+ "retrieval": true,
112
+ "synthesis": true
113
+ }
114
+ },
115
+ {
116
+ "id": "06_recency_temporal",
117
+ "passed": true,
118
+ "verdict": {
119
+ "rails": {
120
+ "factual": "pass",
121
+ "citation": "pass",
122
+ "completeness": "pass",
123
+ "source_quality": "pass",
124
+ "efficiency": "pass"
125
+ },
126
+ "pass_rate": 1.0,
127
+ "passed": true,
128
+ "rationale": "deterministic offline rubric"
129
+ },
130
+ "components": {
131
+ "decompose": true,
132
+ "retrieval": true,
133
+ "synthesis": true
134
+ }
135
+ },
136
+ {
137
+ "id": "07_breadth_list",
138
+ "passed": true,
139
+ "verdict": {
140
+ "rails": {
141
+ "factual": "pass",
142
+ "citation": "pass",
143
+ "completeness": "pass",
144
+ "source_quality": "pass",
145
+ "efficiency": "pass"
146
+ },
147
+ "pass_rate": 1.0,
148
+ "passed": true,
149
+ "rationale": "deterministic offline rubric"
150
+ },
151
+ "components": {
152
+ "decompose": true,
153
+ "retrieval": true,
154
+ "synthesis": true
155
+ }
156
+ },
157
+ {
158
+ "id": "08_numeric_precise",
159
+ "passed": true,
160
+ "verdict": {
161
+ "rails": {
162
+ "factual": "pass",
163
+ "citation": "pass",
164
+ "completeness": "pass",
165
+ "source_quality": "pass",
166
+ "efficiency": "pass"
167
+ },
168
+ "pass_rate": 1.0,
169
+ "passed": true,
170
+ "rationale": "deterministic offline rubric"
171
+ },
172
+ "components": {
173
+ "decompose": true,
174
+ "retrieval": true,
175
+ "synthesis": true
176
+ }
177
+ }
178
+ ]
179
+ }