@tyroneross/build-loop 0.30.3

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 (389) hide show
  1. package/.agents/plugins/marketplace.json +10 -0
  2. package/.claude-plugin/marketplace.json +38 -0
  3. package/.claude-plugin/plugin.json +30 -0
  4. package/.codex-plugin/plugin.json +41 -0
  5. package/.cursor/rules/build-loop-surface.mdc +22 -0
  6. package/AGENTS.md +498 -0
  7. package/LICENSE +202 -0
  8. package/README.md +403 -0
  9. package/agents/alignment-checker.md +124 -0
  10. package/agents/api-assessor.md +136 -0
  11. package/agents/architecture-scout.md +185 -0
  12. package/agents/assessment-orchestrator.md +163 -0
  13. package/agents/build-orchestrator.md +198 -0
  14. package/agents/database-assessor.md +133 -0
  15. package/agents/design-contract-specialist.md +239 -0
  16. package/agents/fact-checker.md +69 -0
  17. package/agents/fix-critique.md +161 -0
  18. package/agents/frontend-assessor.md +136 -0
  19. package/agents/implementer.md +185 -0
  20. package/agents/independent-auditor.md +143 -0
  21. package/agents/mock-scanner.md +78 -0
  22. package/agents/optimize-runner.md +112 -0
  23. package/agents/overfitting-reviewer.md +96 -0
  24. package/agents/performance-assessor.md +159 -0
  25. package/agents/plan-critic.md +85 -0
  26. package/agents/promotion-reviewer.md +121 -0
  27. package/agents/recurring-pattern-detector.md +248 -0
  28. package/agents/retrospective-synthesizer.md +109 -0
  29. package/agents/root-cause-investigator.md +285 -0
  30. package/agents/scope-auditor.md +121 -0
  31. package/agents/security-reviewer.md +151 -0
  32. package/agents/self-improvement-architect.md +125 -0
  33. package/agents/synthesis-critic.md +99 -0
  34. package/agents/transcript-pattern-miner.md +165 -0
  35. package/agents/ui-validator.md +193 -0
  36. package/codex-skills/build-loop/SKILL.md +20 -0
  37. package/codex-skills/debug-loop/SKILL.md +18 -0
  38. package/codex-skills/handoff/SKILL.md +22 -0
  39. package/codex-skills/knowledge/SKILL.md +21 -0
  40. package/codex-skills/optimize/SKILL.md +18 -0
  41. package/codex-skills/research/SKILL.md +18 -0
  42. package/commands/agent-rally-point.md +301 -0
  43. package/commands/assess.md +41 -0
  44. package/commands/debug.md +20 -0
  45. package/commands/debugger-detail.md +34 -0
  46. package/commands/debugger-scan.md +15 -0
  47. package/commands/debugger-status.md +20 -0
  48. package/commands/debugger.md +32 -0
  49. package/commands/handoff.md +113 -0
  50. package/commands/knowledge-review.md +16 -0
  51. package/commands/optimize.md +31 -0
  52. package/commands/plan-verify.md +39 -0
  53. package/commands/promote-experiment.md +129 -0
  54. package/commands/research.md +22 -0
  55. package/commands/run.md +12 -0
  56. package/commands/self-review.md +51 -0
  57. package/commands/start-prd.md +57 -0
  58. package/commands/test.md +34 -0
  59. package/dist/src/assessment-orchestrator.d.ts +78 -0
  60. package/dist/src/assessment-orchestrator.d.ts.map +1 -0
  61. package/dist/src/assessment-orchestrator.js +304 -0
  62. package/dist/src/assessment-orchestrator.js.map +1 -0
  63. package/dist/src/audit-miner.d.ts +21 -0
  64. package/dist/src/audit-miner.d.ts.map +1 -0
  65. package/dist/src/audit-miner.js +421 -0
  66. package/dist/src/audit-miner.js.map +1 -0
  67. package/dist/src/batch-operations.d.ts +30 -0
  68. package/dist/src/batch-operations.d.ts.map +1 -0
  69. package/dist/src/batch-operations.js +339 -0
  70. package/dist/src/batch-operations.js.map +1 -0
  71. package/dist/src/config.d.ts +83 -0
  72. package/dist/src/config.d.ts.map +1 -0
  73. package/dist/src/config.js +176 -0
  74. package/dist/src/config.js.map +1 -0
  75. package/dist/src/context-engine.d.ts +45 -0
  76. package/dist/src/context-engine.d.ts.map +1 -0
  77. package/dist/src/context-engine.js +135 -0
  78. package/dist/src/context-engine.js.map +1 -0
  79. package/dist/src/debug-wrapper.d.ts +58 -0
  80. package/dist/src/debug-wrapper.d.ts.map +1 -0
  81. package/dist/src/debug-wrapper.js +285 -0
  82. package/dist/src/debug-wrapper.js.map +1 -0
  83. package/dist/src/index.d.ts +31 -0
  84. package/dist/src/index.d.ts.map +1 -0
  85. package/dist/src/index.js +149 -0
  86. package/dist/src/index.js.map +1 -0
  87. package/dist/src/interactive-verifier.d.ts +26 -0
  88. package/dist/src/interactive-verifier.d.ts.map +1 -0
  89. package/dist/src/interactive-verifier.js +435 -0
  90. package/dist/src/interactive-verifier.js.map +1 -0
  91. package/dist/src/lessons.d.ts +59 -0
  92. package/dist/src/lessons.d.ts.map +1 -0
  93. package/dist/src/lessons.js +172 -0
  94. package/dist/src/lessons.js.map +1 -0
  95. package/dist/src/log-reader.d.ts +39 -0
  96. package/dist/src/log-reader.d.ts.map +1 -0
  97. package/dist/src/log-reader.js +291 -0
  98. package/dist/src/log-reader.js.map +1 -0
  99. package/dist/src/logger.d.ts +59 -0
  100. package/dist/src/logger.d.ts.map +1 -0
  101. package/dist/src/logger.js +328 -0
  102. package/dist/src/logger.js.map +1 -0
  103. package/dist/src/parallel-retrieval.d.ts +52 -0
  104. package/dist/src/parallel-retrieval.d.ts.map +1 -0
  105. package/dist/src/parallel-retrieval.js +309 -0
  106. package/dist/src/parallel-retrieval.js.map +1 -0
  107. package/dist/src/pattern-extractor.d.ts +30 -0
  108. package/dist/src/pattern-extractor.d.ts.map +1 -0
  109. package/dist/src/pattern-extractor.js +386 -0
  110. package/dist/src/pattern-extractor.js.map +1 -0
  111. package/dist/src/result-aggregator.d.ts +44 -0
  112. package/dist/src/result-aggregator.d.ts.map +1 -0
  113. package/dist/src/result-aggregator.js +315 -0
  114. package/dist/src/result-aggregator.js.map +1 -0
  115. package/dist/src/retrieval.d.ts +139 -0
  116. package/dist/src/retrieval.d.ts.map +1 -0
  117. package/dist/src/retrieval.js +724 -0
  118. package/dist/src/retrieval.js.map +1 -0
  119. package/dist/src/setup/auto-setup.d.ts +15 -0
  120. package/dist/src/setup/auto-setup.d.ts.map +1 -0
  121. package/dist/src/setup/auto-setup.js +157 -0
  122. package/dist/src/setup/auto-setup.js.map +1 -0
  123. package/dist/src/setup/configure-hooks.d.ts +8 -0
  124. package/dist/src/setup/configure-hooks.d.ts.map +1 -0
  125. package/dist/src/setup/configure-hooks.js +16 -0
  126. package/dist/src/setup/configure-hooks.js.map +1 -0
  127. package/dist/src/setup/create-slash-commands.d.ts +8 -0
  128. package/dist/src/setup/create-slash-commands.d.ts.map +1 -0
  129. package/dist/src/setup/create-slash-commands.js +101 -0
  130. package/dist/src/setup/create-slash-commands.js.map +1 -0
  131. package/dist/src/setup/index.d.ts +8 -0
  132. package/dist/src/setup/index.d.ts.map +1 -0
  133. package/dist/src/setup/index.js +17 -0
  134. package/dist/src/setup/index.js.map +1 -0
  135. package/dist/src/setup/inject-claude-md.d.ts +10 -0
  136. package/dist/src/setup/inject-claude-md.d.ts.map +1 -0
  137. package/dist/src/setup/inject-claude-md.js +150 -0
  138. package/dist/src/setup/inject-claude-md.js.map +1 -0
  139. package/dist/src/setup/uninstall.d.ts +22 -0
  140. package/dist/src/setup/uninstall.d.ts.map +1 -0
  141. package/dist/src/setup/uninstall.js +235 -0
  142. package/dist/src/setup/uninstall.js.map +1 -0
  143. package/dist/src/storage.d.ts +192 -0
  144. package/dist/src/storage.d.ts.map +1 -0
  145. package/dist/src/storage.js +1037 -0
  146. package/dist/src/storage.js.map +1 -0
  147. package/dist/src/string-similarity.d.ts +8 -0
  148. package/dist/src/string-similarity.d.ts.map +1 -0
  149. package/dist/src/string-similarity.js +57 -0
  150. package/dist/src/string-similarity.js.map +1 -0
  151. package/dist/src/traces/adapters/browser.d.ts +69 -0
  152. package/dist/src/traces/adapters/browser.d.ts.map +1 -0
  153. package/dist/src/traces/adapters/browser.js +280 -0
  154. package/dist/src/traces/adapters/browser.js.map +1 -0
  155. package/dist/src/traces/adapters/index.d.ts +23 -0
  156. package/dist/src/traces/adapters/index.d.ts.map +1 -0
  157. package/dist/src/traces/adapters/index.js +67 -0
  158. package/dist/src/traces/adapters/index.js.map +1 -0
  159. package/dist/src/traces/adapters/langchain.d.ts +42 -0
  160. package/dist/src/traces/adapters/langchain.d.ts.map +1 -0
  161. package/dist/src/traces/adapters/langchain.js +151 -0
  162. package/dist/src/traces/adapters/langchain.js.map +1 -0
  163. package/dist/src/traces/adapters/opentelemetry.d.ts +46 -0
  164. package/dist/src/traces/adapters/opentelemetry.d.ts.map +1 -0
  165. package/dist/src/traces/adapters/opentelemetry.js +164 -0
  166. package/dist/src/traces/adapters/opentelemetry.js.map +1 -0
  167. package/dist/src/traces/adapters/sentry.d.ts +58 -0
  168. package/dist/src/traces/adapters/sentry.d.ts.map +1 -0
  169. package/dist/src/traces/adapters/sentry.js +212 -0
  170. package/dist/src/traces/adapters/sentry.js.map +1 -0
  171. package/dist/src/traces/index.d.ts +10 -0
  172. package/dist/src/traces/index.d.ts.map +1 -0
  173. package/dist/src/traces/index.js +45 -0
  174. package/dist/src/traces/index.js.map +1 -0
  175. package/dist/src/traces/storage.d.ts +78 -0
  176. package/dist/src/traces/storage.d.ts.map +1 -0
  177. package/dist/src/traces/storage.js +399 -0
  178. package/dist/src/traces/storage.js.map +1 -0
  179. package/dist/src/traces/summarizer.d.ts +83 -0
  180. package/dist/src/traces/summarizer.d.ts.map +1 -0
  181. package/dist/src/traces/summarizer.js +247 -0
  182. package/dist/src/traces/summarizer.js.map +1 -0
  183. package/dist/src/traces/types.d.ts +365 -0
  184. package/dist/src/traces/types.d.ts.map +1 -0
  185. package/dist/src/traces/types.js +14 -0
  186. package/dist/src/traces/types.js.map +1 -0
  187. package/dist/src/types.d.ts +495 -0
  188. package/dist/src/types.d.ts.map +1 -0
  189. package/dist/src/types.js +27 -0
  190. package/dist/src/types.js.map +1 -0
  191. package/docs/agent-surface-policy.md +45 -0
  192. package/docs/memory-setup.md +226 -0
  193. package/hooks/_arch_scan_bg.py +88 -0
  194. package/hooks/_plugin_drift_check_bg.py +85 -0
  195. package/hooks/_session_start_lib.sh +42 -0
  196. package/hooks/git/pre-push +141 -0
  197. package/hooks/hooks.json +126 -0
  198. package/hooks/post-commit +30 -0
  199. package/hooks/pre-commit +29 -0
  200. package/hooks/pre-edit-apppulse.sh +6 -0
  201. package/hooks/pre-edit-architecture.sh +106 -0
  202. package/hooks/pre-edit-rally-point.sh +92 -0
  203. package/hooks/session-start-apppulse.sh +6 -0
  204. package/hooks/session-start-architecture.sh +10 -0
  205. package/hooks/session-start-git-hooks.sh +12 -0
  206. package/hooks/session-start-plugin-drift.sh +27 -0
  207. package/hooks/session-start-rally-point.sh +44 -0
  208. package/hooks/session-start-retrieval.sh +24 -0
  209. package/hooks/session-start-worktree-gc.sh +105 -0
  210. package/hooks/test_apppulse_hooks.sh +6 -0
  211. package/hooks/test_rally_point_hooks.sh +388 -0
  212. package/hooks/test_sec008_hooks.sh +66 -0
  213. package/package.json +111 -0
  214. package/scripts/README.md +301 -0
  215. package/scripts/_db_url.py +61 -0
  216. package/scripts/_paths.py +445 -0
  217. package/scripts/blm.py +162 -0
  218. package/scripts/blm_api.py +263 -0
  219. package/scripts/check_cache_sync.py +490 -0
  220. package/scripts/embed_backend.py +475 -0
  221. package/scripts/install_memory.py +556 -0
  222. package/scripts/lessons_index/__init__.py +77 -0
  223. package/scripts/lessons_index/__main__.py +135 -0
  224. package/scripts/lessons_index/ingest.py +288 -0
  225. package/scripts/lessons_index/query.py +285 -0
  226. package/scripts/lessons_index/schema.py +114 -0
  227. package/scripts/memory_context/__init__.py +610 -0
  228. package/scripts/memory_graph/__init__.py +393 -0
  229. package/scripts/project_resolver.py +172 -0
  230. package/scripts/prune_codex_plugin_cache.py +23 -0
  231. package/scripts/prune_plugin_cache.py +359 -0
  232. package/scripts/semantic_index/__init__.py +482 -0
  233. package/scripts/semantic_index/_bench_hybrid.py +175 -0
  234. package/scripts/semantic_index/backfill.py +263 -0
  235. package/scripts/semantic_index/hybrid.py +174 -0
  236. package/scripts/semantic_index/test_backfill.py +292 -0
  237. package/scripts/semantic_index/test_hybrid.py +541 -0
  238. package/scripts/sync_navgator_lessons.py +666 -0
  239. package/scripts/sync_plugin_cache.py +388 -0
  240. package/skills/agent-rally-point/SKILL.md +165 -0
  241. package/skills/agent-rally-watcher/SKILL.md +83 -0
  242. package/skills/api-registry-bridge/SKILL.md +59 -0
  243. package/skills/architecture/dead/SKILL.md +56 -0
  244. package/skills/architecture/impact/SKILL.md +88 -0
  245. package/skills/architecture/review/SKILL.md +227 -0
  246. package/skills/architecture/rules/SKILL.md +72 -0
  247. package/skills/architecture/scan/SKILL.md +79 -0
  248. package/skills/architecture/trace/SKILL.md +65 -0
  249. package/skills/attribution-standard/SKILL.md +122 -0
  250. package/skills/authentication/SKILL.md +81 -0
  251. package/skills/authentication/references/better-auth-magic-link.md +135 -0
  252. package/skills/authentication/references/better-auth-setup.md +224 -0
  253. package/skills/authentication/references/google-calendar-sync.md +278 -0
  254. package/skills/authentication/references/google-cloud-console.md +159 -0
  255. package/skills/authentication/references/google-geocoding-directions.md +126 -0
  256. package/skills/authentication/references/google-lessons-example-web-app.md +177 -0
  257. package/skills/authentication/references/google-maps.md +162 -0
  258. package/skills/authentication/references/google-oauth-setup.md +343 -0
  259. package/skills/authentication/references/google-places.md +173 -0
  260. package/skills/authentication/references/lessons-example-web-app-better-auth.md +126 -0
  261. package/skills/authentication/references/resend-email.md +227 -0
  262. package/skills/authentication/references/resend-otp-magic-link.md +153 -0
  263. package/skills/authentication/references/supabase-auth.md +172 -0
  264. package/skills/auto-decision-capture/SKILL.md +419 -0
  265. package/skills/build-loop/SKILL.md +516 -0
  266. package/skills/build-loop/detect-plugins.mjs +82 -0
  267. package/skills/build-loop/eval-guide.md +65 -0
  268. package/skills/build-loop/fallbacks.md +549 -0
  269. package/skills/build-loop/phases/fact-check.md +42 -0
  270. package/skills/build-loop/phases/ui-validation.md +267 -0
  271. package/skills/build-loop/references/apple-native-planning.md +439 -0
  272. package/skills/build-loop/references/capability-routing.md +181 -0
  273. package/skills/build-loop/references/codex-subagents.md +98 -0
  274. package/skills/build-loop/references/coordination.md +161 -0
  275. package/skills/build-loop/references/correction-aware-capture.md +177 -0
  276. package/skills/build-loop/references/independent-auditor.md +72 -0
  277. package/skills/build-loop/references/intent-capability-pack.md +170 -0
  278. package/skills/build-loop/references/intent-exploration-prompts.md +96 -0
  279. package/skills/build-loop/references/leadership.md +72 -0
  280. package/skills/build-loop/references/memory.md +284 -0
  281. package/skills/build-loop/references/modular-systems-pack.md +96 -0
  282. package/skills/build-loop/references/output-style.md +129 -0
  283. package/skills/build-loop/references/pay-it-forward-arch.md +98 -0
  284. package/skills/build-loop/references/phase-1-assess.md +218 -0
  285. package/skills/build-loop/references/phase-2-plan.md +77 -0
  286. package/skills/build-loop/references/phase-3-execute.md +47 -0
  287. package/skills/build-loop/references/phase-4-review.md +310 -0
  288. package/skills/build-loop/references/phase-5-iterate.md +71 -0
  289. package/skills/build-loop/references/phase-6-learn.md +56 -0
  290. package/skills/build-loop/references/recent-design-structures.md +274 -0
  291. package/skills/build-loop/references/refactor-history/ASSESSMENT.md +85 -0
  292. package/skills/build-loop/references/refactor-history/STANDALONE_TEST_RUN.md +149 -0
  293. package/skills/build-loop/references/refactor-history/scenarios/01-simple-bugfix.md +32 -0
  294. package/skills/build-loop/references/refactor-history/scenarios/02-ui-build-with-iteration.md +48 -0
  295. package/skills/build-loop/references/refactor-history/scenarios/03-multi-failure-escalation.md +60 -0
  296. package/skills/build-loop/references/refactor-history/scenarios/04-ui-build-ibr-absent.md +51 -0
  297. package/skills/build-loop/references/refactor-history/scenarios/05-refactor-navgator-absent.md +71 -0
  298. package/skills/build-loop/references/refactor-history/scenarios/06-recurring-bug-debugger-absent.md +52 -0
  299. package/skills/build-loop/references/refactor-history/trace-comparison.md +202 -0
  300. package/skills/build-loop/references/self-recursive-dev.md +77 -0
  301. package/skills/build-loop/references/self-review.md +232 -0
  302. package/skills/build-loop/references/ui-io-contract.md +116 -0
  303. package/skills/build-loop/scanners/audit-design-rules.mjs +476 -0
  304. package/skills/build-loop/scanners/require-visual-evidence.mjs +239 -0
  305. package/skills/build-loop/templates/backlog-item.md +35 -0
  306. package/skills/build-loop/templates/codex-worker-prompt.md +89 -0
  307. package/skills/build-loop/templates/ui-subagent-prompt.md +179 -0
  308. package/skills/build-loop/templates/ux-fix-plan.md +40 -0
  309. package/skills/building-with-deepagents/SKILL.md +195 -0
  310. package/skills/building-with-deepagents/references/anti-patterns.md +141 -0
  311. package/skills/building-with-deepagents/references/api-quick-reference.md +184 -0
  312. package/skills/capabilities/SKILL.md +91 -0
  313. package/skills/debug-loop/SKILL.md +278 -0
  314. package/skills/debug-loop/references/convergence-rules.md +145 -0
  315. package/skills/debug-loop/references/swiftui-macos-clunky-ui-debugger.html +823 -0
  316. package/skills/debugging/assess/SKILL.md +111 -0
  317. package/skills/debugging/debug-loop/SKILL.md +231 -0
  318. package/skills/debugging/memory/SKILL.md +147 -0
  319. package/skills/debugging/store/SKILL.md +153 -0
  320. package/skills/debugging-memory/SKILL.md +440 -0
  321. package/skills/debugging-memory/examples/incident-example.json +67 -0
  322. package/skills/debugging-memory/references/incident-documentation.md +280 -0
  323. package/skills/debugging-memory/references/ios-notification-alarm-playbook.md +300 -0
  324. package/skills/debugging-memory/references/pattern-extraction.md +163 -0
  325. package/skills/debugging-memory/references/subagent-integration.md +312 -0
  326. package/skills/defenseclaw-bridge/SKILL.md +156 -0
  327. package/skills/defenseclaw-bridge/references/dc-config-mapping.md +162 -0
  328. package/skills/defenseclaw-bridge/references/output-format.md +108 -0
  329. package/skills/handoff/SKILL.md +94 -0
  330. package/skills/ibr-bridge/SKILL.md +68 -0
  331. package/skills/knowledge/SKILL.md +127 -0
  332. package/skills/knowledge/references/recall-integration.md +83 -0
  333. package/skills/knowledge/templates/madr-minimal.md +56 -0
  334. package/skills/knowledge-review/SKILL.md +112 -0
  335. package/skills/logging-tracer/SKILL.md +202 -0
  336. package/skills/logging-tracer/references/log-analysis.md +128 -0
  337. package/skills/logging-tracer/references/stack-templates.md +274 -0
  338. package/skills/logging-tracer-bridge/SKILL.md +70 -0
  339. package/skills/mcp-builder/SKILL.md +278 -0
  340. package/skills/mcp-builder/references/mcp-security.md +106 -0
  341. package/skills/model-tiering/SKILL.md +216 -0
  342. package/skills/native-ax-driver/SKILL.md +220 -0
  343. package/skills/native-ax-driver/scripts/layout_fill.py +222 -0
  344. package/skills/native-ax-driver/scripts/native_driver.py +495 -0
  345. package/skills/native-ax-driver/swift/bl-ax-driver/Package.swift +20 -0
  346. package/skills/native-ax-driver/swift/bl-ax-driver/Sources/main.swift +542 -0
  347. package/skills/optimize/SKILL.md +202 -0
  348. package/skills/optimize/profiles.md +61 -0
  349. package/skills/plan-verify/SKILL.md +115 -0
  350. package/skills/plan-verify/test-fixtures/example-app-v20-findings.json +27 -0
  351. package/skills/plan-verify/test-fixtures/example-app-v20.md +42 -0
  352. package/skills/plan-verify/test-fixtures/example-app-v22-findings.json +15 -0
  353. package/skills/plan-verify/test-fixtures/example-app-v22.md +1608 -0
  354. package/skills/plan-verify/test-fixtures/unrelated-good-plan-findings.json +15 -0
  355. package/skills/plan-verify/test-fixtures/unrelated-good-plan.md +55 -0
  356. package/skills/plugin-builder/SKILL.md +329 -0
  357. package/skills/plugin-builder/references/authoritative-sources.md +104 -0
  358. package/skills/plugin-builder/references/build-loop-phase-guidance.md +94 -0
  359. package/skills/plugin-builder/references/components-guide.md +221 -0
  360. package/skills/plugin-builder/references/distribution.md +317 -0
  361. package/skills/plugin-builder/references/dual-host-claude-codex.md +220 -0
  362. package/skills/plugin-builder/references/hooks-reference.md +210 -0
  363. package/skills/plugin-builder/references/manifest-schema.md +111 -0
  364. package/skills/plugin-builder/references/plugin-hygiene-lessons.md +307 -0
  365. package/skills/plugin-tests/SKILL.md +94 -0
  366. package/skills/prd-bridge/SKILL.md +135 -0
  367. package/skills/research/SKILL.md +80 -0
  368. package/skills/security-methodology/SKILL.md +105 -0
  369. package/skills/security-methodology/references/cross-source-matrix.md +77 -0
  370. package/skills/security-methodology/references/mitre-atlas-starter.md +83 -0
  371. package/skills/security-methodology/references/nist-600-1-mapping.md +119 -0
  372. package/skills/security-methodology/references/owasp-agentic-top-10.md +157 -0
  373. package/skills/security-methodology/references/owasp-llm-top-10.md +144 -0
  374. package/skills/security-methodology/references/owasp-web-top-10.md +65 -0
  375. package/skills/self-improve/SKILL.md +298 -0
  376. package/skills/spec-writing/SKILL.md +548 -0
  377. package/skills/spec-writing/scripts/check_checklist.py +478 -0
  378. package/skills/sync-skills/SKILL.md +132 -0
  379. package/skills/telemetry/SKILL.md +66 -0
  380. package/skills/ui-design/SKILL.md +75 -0
  381. package/skills/ui-design/references/design-patterns-multi.md +165 -0
  382. package/skills/ui-design/references/design-preferences-from-owned-apps.md +150 -0
  383. package/skills/ui-design/references/evidence-capture-policy.md +55 -0
  384. package/skills/ui-design/references/ui-guidance-sources.md +330 -0
  385. package/skills/ui-design/references/universal-design-principles.alt.md +182 -0
  386. package/skills/ui-design/references/universal-design-principles.md +419 -0
  387. package/templates/memory/MEMORY.md.template +55 -0
  388. package/templates/memory/constitution.md.template +64 -0
  389. package/templates/memory/manifest.json +47 -0
@@ -0,0 +1,1608 @@
1
+ <!-- SPDX-FileCopyrightText: 2025-2026 Tyrone Ross, Jr <46267523+tyroneross@users.noreply.github.com> | SPDX-License-Identifier: Apache-2.0 -->
2
+
3
+ override: reads-from-dependency — pre-dates the reads-from section requirement; fixture demonstrates other rules only
4
+
5
+ # Example App — Unified AI Search Architecture (Full Plan)
6
+
7
+ **Version:** 2.2 — SOTA research integrated (RRF, cross-encoder reranker, CRAG, multi-query, HippoRAG)
8
+ **Last updated:** 2026-04-28
9
+ **Working directory:** `/Users/you/example-app/`
10
+ **Provider priority:** Accuracy first → speed → cost. Stay on Groq throughout (cheaper + faster than OpenAI/Anthropic for comparable workloads, ~5–10× cost advantage, 3–12× speed advantage). **Pick the right Groq model per task** — strict-schema work (Tier 3 intent classifier) uses `openai/gpt-oss-120b` (constrained decoding, 100% schema-valid); synthesis uses Llama 3.3 70B; fast-path classification uses Llama 3.1 8B. Cross-provider swaps (OpenAI Structured Outputs, dedicated rerankers like Zerank-2 / Voyage 2.5 / Jina v3) stay deferred to a final optimization phase **only after the architecture is working end-to-end**, per user direction. Per-call `max_tokens` and per-run `budget` set in the provider-config block; new LLM calls inherit that ceiling. **Threat model:** PII handling (§13.2) covered by hash-only-store control; new tier-2 embed call mapped to OWASP LLM01 / LLM06 review at top of §11. See `security-methodology` skill for the full OWASP / ASI ID matrix.
11
+
12
+ > **Codex review applied 2026-04-28** — five errors corrected, scope tightened to Milestone 1 (format-intent + structured-results) merge-able alone, Milestone 2 (consolidations) explicitly deferred. Full change log at §11.
13
+ > **SOTA research applied 2026-04-28 (v2.2)** — `/build-loop:research` validated the plan's architectural shape as canonical, but surfaced three implementation-layer upgrades that materially lift quality at modest cost: Reciprocal Rank Fusion (RRF) in hybrid retrieval, a dedicated cross-encoder reranker (replacing LLM-as-reranker), and a CRAG-style self-correction gate. Full change log at §13.
14
+
15
+ ---
16
+
17
+ ## Intent (plain language — read this first)
18
+
19
+ **What this plan is.** A blueprint for fixing one specific user-visible bug — the AI search silently throws away format instructions like "share this in a table" — by addressing the root architectural cause rather than papering over the symptom.
20
+
21
+ **Why the small bug needs a big plan.** The format-drop happens because three separate parts of the codebase each have their own opinion about "what is the user asking for", none of them agree on a schema, and the synthesis prompt is contractually forbidden from emitting anything but markdown. Adding `if (query.includes("table"))` somewhere only delays the next dropped intent. A typed pipeline that runs the same shape end-to-end is the actual fix.
22
+
23
+ **What changes.** The application layer (Vercel API routes, frontend search components, intent and synthesis libraries). One new front door at `/api/search`, one typed `Intent`, one deterministic planner that fans out to the three RAG patterns the codebase already implements (Pipeline / Agentic / Knowledge Graph), one normalized response shape that includes table/chart/timeline/graph/unsupported-format as first-class kinds, one consolidated frontend `SearchBar` and `SearchResults` shell.
24
+
25
+ **What does not change.** Postgres schema, Railway worker topology, Vercel cron schedules, Redis cache discipline, all 40+ Prisma models, RSS ingest pipeline, KG extraction pipeline. Zero infra/deployment changes.
26
+
27
+ **How to ship safely.** Two milestones. Milestone 1 (~5 days) is merge-able alone and fixes the visible bug end-to-end while leaving every existing route untouched (new pipeline runs in parallel behind a flag). Milestone 2 (~4 days) does the cleanups — orphan deletions, route consolidation, frontend de-duplication — only after Milestone 1 has soaked in production.
28
+
29
+ **For another agent picking this up.** Read §0 for the executive frame, §1 for current state, §2 for target state, §5 for the ordered action list, and §11 for what was already wrong and corrected. Each section below opens with its own "Intent of this section" line so you can navigate without reading linearly. Status markers (✅ verified · ⚠️ untested · ❓ uncertain · TAG:INFERRED) are honest about confidence.
30
+
31
+ **Working assumptions baked in.**
32
+ - The two failing queries (`"…share this in a table"` and `"create a tornado diagram of recent news trends"`) are representative, not edge cases. Other format directives are dropped by the same mechanism.
33
+ - Postgres has all the data needed; the gap is at the application layer.
34
+ - Existing tests, eval harness, and circuit-breaker patterns can be extended rather than replaced.
35
+ - Caller maps for ambiguous routes must be verified by template-literal-aware grep, not just NavGator (NavGator misses template-literal `fetch()` calls — see §11.2).
36
+
37
+ ---
38
+
39
+ ---
40
+
41
+ ## 0. Executive Summary
42
+
43
+ > **Intent of this section:** Hand a stakeholder enough context in one screen to decide whether to greenlight the work. Names the bug, names the root cause, names the cost, names the milestone split.
44
+
45
+ Two real user queries silently dropped their format directives:
46
+
47
+ | Query | Format requested | What rendered |
48
+ |---|---|---|
49
+ | "What's the latest AI trends for research and product releases. **share this in a table**" | table | Markdown pyramid only |
50
+ | "**create a tornado diagram** of recent news trends" | tornado diagram | Markdown pyramid only |
51
+
52
+ The visible bug is "format intent silently dropped." Investigation surfaced a much larger architectural problem:
53
+
54
+ - **60+ overlapping search/retrieval/synthesis API routes** on Vercel
55
+ - **Regex intent detection scattered across three places**, none authoritative
56
+ - **Hard partitioning** of general / research / KG / trending data with no server-side fan-out
57
+ - **19+ search components** in the frontend (V3, V7D, base, modal variants)
58
+ - **Two of six "orphan" routes are actually high-value engineering** that was abandoned mid-migration (HyDE expansion, SSE streaming) — *trending-topics-v2 was misclassified as orphan; it has 3 live callers*
59
+ - **An eval harness and cascade scaffolding already exist** — just disconnected
60
+
61
+ **This plan unifies everything** behind one front door, a 3-tier intent cascade, a deterministic query planner that fans out to Pipeline / Agentic / KG RAG patterns, and a normalized response shape that treats format (`table`, `chart`, `unsupported-format`) as a first-class field.
62
+
63
+ ### Two-milestone delivery
64
+
65
+ **Milestone 1 — Format intent + structured results + retrieval-quality lift (merge-able alone)** · ~8 days
66
+ The high-value, low-risk slice. Ships the visible bug fix end-to-end without touching live routes, **and** lifts retrieval quality with three SOTA additions (v2.2):
67
+ - Eval harness incl. RAGAS metrics (P0), 3-tier intent cascade in shadow mode with **OpenAI Structured Outputs for Tier 3** (P1-P2), structured-result kinds + UI (P3), planner extraction with **RRF hybrid retrieval + cross-encoder reranker + CRAG quality gate** (P4), `/api/search` orchestrator behind a flag (P5), prompt governance (P8 partial), telemetry (P9 partial).
68
+ - Old routes keep working; new pipeline runs in parallel.
69
+
70
+ **Milestone 2 — Consolidations, cleanups, and graph quality (defer until M1 has soaked)** · ~6 days
71
+ Higher coordination cost, more caller-map work, plus deeper retrieval upgrades:
72
+ - **Multi-query / RAG-Fusion as primary rewrite** (P5b extended), HyDE remains as vague-query fallback. SSE recovery (P5c), trending v1↔v2 reconciliation (P5d), orphan deletes (P6), trend-dict extract (P6b), Tier 2 embedding classifier (P7), full prompt-builder governance (P8), full telemetry (P9), **HippoRAG-style PPR over `entity_pairs` for KG-RAG (P5e)**.
73
+ - Each step gated by a strict caller-map cross-check (NavGator misses template literals — see §11.2).
74
+
75
+ **Total focused engineering: ~14 days** across 12 phases, every phase reversible behind a feature flag, zero infra/deployment changes required for cutover. (Up from ~9 days in v2.1; the +5 days buys substantially higher retrieval quality and removes the single-vendor dependency on Groq 70B for reranking.)
76
+
77
+ ---
78
+
79
+ ## 1. BEFORE — Current Architecture
80
+
81
+ > **Intent of this section:** Show the full system as it exists today so a new reader can orient before any change is proposed. Walks Frontend → Vercel functions → Postgres / Redis / Railway, then enumerates the 12 verified pain points with file:line evidence. If you only need to know "what's there now", read this section and stop.
82
+
83
+ ### 1.1 Layer overview
84
+
85
+ ```
86
+ ┌──────────────────────────────────────────────────────────────────────┐
87
+ │ FRONTEND (Vercel Edge / CDN) │
88
+ │ │
89
+ │ 19+ search components, 5+ search pages, 4+ overlapping versions │
90
+ │ │
91
+ │ IntelligentSearchBar.tsx ──┐ │
92
+ │ V7DSearchBar.tsx ├─► fetch('/api/intelligent-search') │
93
+ │ V3SearchInput.tsx │ fetch('/api/search') │
94
+ │ SearchModalNew.tsx ────────┤ (also '/api/intelligent-search') │
95
+ │ SearchModal.tsx │ fetch('/api/trending-topics') │
96
+ │ GlobalSearchBar.tsx │ fetch('/api/trending-topics-v2') │
97
+ │ SearchBottomSheet.tsx │ ... 60+ endpoints │
98
+ │ ArticleSearchPopout.tsx ───┘ │
99
+ │ FloatingSearchPill.tsx (KG) │
100
+ │ EntitySearch.tsx (KG) ────────► fetch('/api/kg/entities/search') │
101
+ │ KGSearchInterface.tsx (KG) │
102
+ │ EntityRelationshipPanel (KG) │
103
+ │ │
104
+ │ Pages mounting search: │
105
+ │ /search, /markettrends, /digest, /graph, /admin, /versions │
106
+ └──────────────────────────────────────────────────────────────────────┘
107
+
108
+
109
+ ┌──────────────────────────────────────────────────────────────────────┐
110
+ │ VERCEL FUNCTIONS (Next.js App Router) │
111
+ │ maxDuration: 60s · 11 cron jobs │
112
+ │ │
113
+ │ ┌──────────────────────┐ ┌──────────────────────┐ │
114
+ │ │ /api/intelligent- │ │ /api/search │ │
115
+ │ │ search/route.ts │ │ route.ts │ │
116
+ │ │ (1582 lines) │ │ (399 lines) │ │
117
+ │ │ │ │ │ │
118
+ │ │ Inline regex intent │ │ Keyword + cache │ │
119
+ │ │ at lines 1170-1181 │ │ (no intent layer) │ │
120
+ │ └──────────┬───────────┘ └──────────┬───────────┘ │
121
+ │ │ │ │
122
+ │ ▼ ▼ │
123
+ │ ┌──────────────────────────────────────────────┐ │
124
+ │ │ lib/search/query-router.ts │ │
125
+ │ │ ⚠ Cascade pattern half-built. Used ONLY by │ │
126
+ │ │ intelligent-search. Other routes bypass. │ │
127
+ │ └──────────────────────────────────────────────┘ │
128
+ │ │ │
129
+ │ ▼ │
130
+ │ ┌──────────────────────────────────────────────┐ │
131
+ │ │ lib/knowledge-graph/ │ │
132
+ │ │ intelligent-query-engine.ts │ │
133
+ │ │ Workhorse — reads 8 DB tables, hides │ │
134
+ │ │ planner logic inside retrieval class │ │
135
+ │ └──────────────────────────────────────────────┘ │
136
+ │ │
137
+ │ Other live routes (selected): │
138
+ │ /api/trending-topics, /api/trending-topics-simple, │
139
+ │ /api/trending-topics-v2 (3 callers — sibling, not orphan) │
140
+ │ /api/research/papers, /api/research/trending │
141
+ │ /api/graph, /api/graph/trends, /api/graph/analytics │
142
+ │ /api/entities/search (general entity search) │
143
+ │ /api/kg/entities/search (KG-UI specific, 3 callers — KEEP) │
144
+ │ /api/comprehensive-summary, /api/executive-summary, │
145
+ │ /api/brief, /api/articles/smart-summary, /api/summarize │
146
+ │ (8+ overlapping summarizers) │
147
+ │ │
148
+ │ Orphans (zero callers — verified by template-literal-aware grep): │
149
+ │ /api/search/semantic ⭐ HyDE expansion (rescue) │
150
+ │ /api/search/stream ⭐ SSE progressive (rescue) │
151
+ │ /api/intelligence ⚠ Math.random() fake numerics (mostly kill) │
152
+ │ /api/ai-summarize 🗑 Superseded │
153
+ │ /api/test-summary 🗑 Test scaffold │
154
+ └──────────────────────────────────────────────────────────────────────┘
155
+
156
+ ┌─────────────────┼─────────────────┐
157
+ ▼ ▼ ▼
158
+ ┌─────────────────────┐ ┌──────────────────┐ ┌─────────────────────┐
159
+ │ POSTGRES │ │ REDIS │ │ RAILWAY WORKERS │
160
+ │ (Supabase / RDS) │ │ (Upstash KV) │ │ 4 services │
161
+ │ │ │ │ │ │
162
+ │ 40+ Prisma models │ │ TrendingCache │ │ bullmq-worker │
163
+ │ Article │ │ temporal-cache │ │ → entities, │
164
+ │ Summary │ │ request_cache │ │ relationships, │
165
+ │ ArticleEmbedding │ │ 5-min TTLs │ │ embeddings, │
166
+ │ (pgvector) │ │ │ │ summaries │
167
+ │ entities │ │ lib/redis- │ │ │
168
+ │ entity_pairs │ │ connection.ts │ │ clustering-worker │
169
+ │ entity_mentions │ │ │ │ → article-clust. │
170
+ │ trending_event_ │ │ │ │ + kg-extraction │
171
+ │ clusters │ │ │ │ │
172
+ │ cluster_trend_ │ │ │ │ trending-worker │
173
+ │ analysis │ │ │ │ → trending-default │
174
+ │ topic_clusters │ │ │ │ │
175
+ │ RssSource │ │ │ │ scraper-worker │
176
+ │ source_credibility │ │ │ │ → scraper-pipeline │
177
+ │ burst_signals │ │ │ │ │
178
+ │ ... │ │ │ │ Self-timer DISABLED│
179
+ │ │ │ │ │ Vercel cron │
180
+ │ Materialized view: │ │ │ │ /api/cron/detect- │
181
+ │ mv_active_ │ │ │ │ trends is sole │
182
+ │ trending_topics │ │ │ │ trigger (every 6h)│
183
+ │ (refreshed 30min │ │ │ │ │
184
+ │ via cron) │ │ │ │ │
185
+ └─────────────────────┘ └──────────────────┘ └─────────────────────┘
186
+ ```
187
+
188
+ ### 1.2 Pain points (verified, file:line citations)
189
+
190
+ | # | Layer | Symptom | Evidence |
191
+ |---|---|---|---|
192
+ | 1 | Vercel API | Format intent dropped | `app/api/intelligent-search/route.ts:1170-1181` regex has no `table\|chart\|tornado` patterns |
193
+ | 2 | Vercel API | Two parallel search routes | Both `/api/intelligent-search` and `/api/search` actively wired; no hierarchy |
194
+ | 3 | Vercel API | 5 confirmed orphan routes (template-literal-aware grep) | 0 callers: `intelligence`, `ai-summarize`, `test-summary`, `search/semantic`, `search/stream`. Two of them — `search/semantic` (HyDE) and `search/stream` (SSE) — are valuable enough to recover. |
195
+ | 4 | Vercel API | 8+ overlapping summarizers | `comprehensive-summary`, `executive-summary`, `executive-summary-optimized`, `brief`, `brief/clusters/[id]/synthesize`, `articles/smart-summary`, `summarize`, `intelligent-search/summary` |
196
+ | 5 | Vercel API | 4 overlapping trending endpoints | `trending-topics`, `trending-topics-simple`, `graph/trends`, `kg/trending`, `research/trending` |
197
+ | 6 | Vercel API | Two entity search routes serve different consumers | `/api/entities/search` (general) and `/api/kg/entities/search` (KG UI: 3 callers in `KGSearchInterface.tsx:103`, `EntitySearch.tsx:127`, `EntityRelationshipPanel.tsx:412`). Not duplicates — keep both, share lib. |
198
+ | 7 | lib | Intent detection scattered | inline regex + `lib/search/query-router.ts` + `lib/knowledge-graph/intelligent-query-engine.ts:80+` |
199
+ | 8 | lib | Synthesis prompt is markdown-only | `app/api/intelligent-search/summary/route.ts` — `OUTPUT FORMAT: valid markdown only` |
200
+ | 9 | lib | Hard domain partition | `entities.domain = 'general' \| 'research'`; client picks upfront, no fan-out |
201
+ | 10 | Frontend | 19+ overlapping search components | V3/V7D/base/modal variants without a single source-of-truth bar |
202
+ | 11 | Orphans | `intelligence/route.ts:87` `Math.random()` | Generates fake stock-impact numbers, violates `feedback_no_fake_stats.md` |
203
+ | 12 | Orphans | HyDE + SSE built, never wired into the UI | Recoverable in Milestone 2 (`search/semantic` + `search/stream`). Multi-mode trending v2 is *not* an orphan — it has 3 live callers; reconciliation is a separate concern. |
204
+
205
+ ---
206
+
207
+ ## 2. AFTER — Target Architecture
208
+
209
+ > **Intent of this section:** Show the same system after the change so the diff to §1 is visible at a glance. The shape is: one front door → typed Intent via 3-tier cascade → deterministic planner → fan-out to three RAG patterns → normalized result kinds → format-aware synthesis. Postgres / Redis / Railway / cron stay structurally identical. Read this with §1 open in a split view.
210
+
211
+ ### 2.1 Layer overview
212
+
213
+ ```
214
+ ┌──────────────────────────────────────────────────────────────────────┐
215
+ │ FRONTEND (Vercel Edge / CDN) │
216
+ │ │
217
+ │ ONE canonical search bar component: │
218
+ │ components/search/SearchBar.tsx │
219
+ │ (consolidated from IntelligentSearchBar + V7D + V3 variants) │
220
+ │ │
221
+ │ ONE results renderer: │
222
+ │ components/search/SearchResults.tsx │
223
+ │ ├─ <PyramidSummary /> (markdown narration) │
224
+ │ ├─ <ResultTable /> (NEW — tabular structured) │
225
+ │ ├─ <ResultChart /> (NEW — bar/timeline/ranked-bar) │
226
+ │ ├─ <ResultGraph /> (existing KG viz) │
227
+ │ └─ <UnsupportedFormat /> (NEW — honest downgrade) │
228
+ │ │
229
+ │ Subscriber for SSE progressive results: │
230
+ │ hooks/useSearchStream.ts (NEW — wraps EventSource) │
231
+ │ │
232
+ │ Page-specific embeds keep their wrappers but call SearchBar: │
233
+ │ /search, /markettrends, /digest, /graph │
234
+ └──────────────────────────────────────────────────────────────────────┘
235
+
236
+ ▼ POST { query, hints?, sessionId? }
237
+ ▼ or GET ?stream=true (SSE)
238
+ ┌──────────────────────────────────────────────────────────────────────┐
239
+ │ VERCEL FUNCTIONS — ONE FRONT DOOR │
240
+ │ │
241
+ │ ┌──────────────────────────────────────────────────┐ │
242
+ │ │ app/api/search/route.ts (NEW — supersedes both) │ │
243
+ │ │ Shared handler: /api/intelligent-search is a │ │
244
+ │ │ thin shim exporting the same runSearch fn — │ │
245
+ │ │ no redirect, no URL change, both routes alive │ │
246
+ │ └──────────────────────┬───────────────────────────┘ │
247
+ │ ▼ │
248
+ │ ┌──────────────────────────────────────────────────┐ │
249
+ │ │ 3-TIER INTENT CASCADE │ │
250
+ │ │ lib/search/intent/ │ │
251
+ │ │ ├─ tier1-regex.ts 5-20ms, conf ≥ 0.85 │ │
252
+ │ │ ├─ tier2-embed.ts 20-50ms, conf ≥ 0.80 │ │
253
+ │ │ └─ tier3-llm.ts 150-600ms, terminal │ │
254
+ │ │ │ │
255
+ │ │ Output: typed Intent { │ │
256
+ │ │ domains: ('articles'|'releases'|'research' │ │
257
+ │ │ |'kg')[], │ │
258
+ │ │ retrievalPattern: 'pipeline'|'agentic' │ │
259
+ │ │ |'kg'|'hybrid', │ │
260
+ │ │ format: 'default'|'table'|'chart'|'timeline' │ │
261
+ │ │ |'graph', │ │
262
+ │ │ chartSubtype?: 'bar'|'ranked-bar'|'tornado', │ │
263
+ │ │ horizonDays, comparisonTargets, │ │
264
+ │ │ topicKeywords, confidence, tier │ │
265
+ │ │ } │ │
266
+ │ └──────────────────────┬───────────────────────────┘ │
267
+ │ ▼ │
268
+ │ ┌──────────────────────────────────────────────────┐ │
269
+ │ │ lib/search/planner.ts (NEW — extracted from │ │
270
+ │ │ intelligent-query-engine.ts) │ │
271
+ │ │ Pure deterministic function: │ │
272
+ │ │ planRetrieval(intent) → RetrievalPlan │ │
273
+ │ └─────┬───────────┬───────────┬────────────┬───────┘ │
274
+ │ ▼ ▼ ▼ ▼ │
275
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────────┐ │
276
+ │ │ PIPELINE │ │ AGENTIC │ │ KG │ │ HYDE │ │
277
+ │ │ RAG │ │ RAG │ │ RAG │ │ EXPANSION │ │
278
+ │ │ articles │ │ multi- │ │ entity │ │ (RECOVERED) │ │
279
+ │ │ +releases│ │ source │ │ graph, │ │ for vague │ │
280
+ │ │ +trending│ │ fan-out │ │ papers, │ │ queries │ │
281
+ │ │ │ │ +merge │ │ relations│ │ │ │
282
+ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └──────┬──────┘ │
283
+ │ └─────────────┼─────────────┼───────────────┘ │
284
+ │ ▼ ▼ │
285
+ │ ┌──────────────────────────────────────────────────┐ │
286
+ │ │ lib/search/normalize.ts (NEW) │ │
287
+ │ │ Builds StructuredResult[]: │ │
288
+ │ │ { kind:'table', columns, rows, source } │ │
289
+ │ │ { kind:'chart', subtype, series, source } │ │
290
+ │ │ { kind:'timeline', events, source } │ │
291
+ │ │ { kind:'graph', nodes, edges, source } │ │
292
+ │ │ { kind:'unsupported-format', │ │
293
+ │ │ requested, fallbackKind, reason } │ │
294
+ │ └──────────────────────┬───────────────────────────┘ │
295
+ │ ▼ │
296
+ │ ┌──────────────────────────────────────────────────┐ │
297
+ │ │ lib/search/synthesizer.ts │ │
298
+ │ │ (refactored from /summary/route.ts) │ │
299
+ │ │ Format-aware: 1-paragraph when tables present, │ │
300
+ │ │ full pyramid when format=default │ │
301
+ │ │ Reads prompts from prompt library, NOT inline │ │
302
+ │ └──────────────────────┬───────────────────────────┘ │
303
+ │ ▼ │
304
+ │ Response (JSON or SSE chunks) │
305
+ │ │
306
+ │ Other API routes (UNCHANGED, just deduped): │
307
+ │ - /api/research/* (kept as KG RAG implementation detail) │
308
+ │ - /api/graph/* (kept as KG visualization) │
309
+ │ - /api/trending-topics (consolidated with v2 features) │
310
+ │ - /api/cron/* (unchanged — Vercel cron triggers Railway workers) │
311
+ │ │
312
+ │ Milestone 2 deletes (5 zero-caller routes only, AFTER recovery): │
313
+ │ intelligence, ai-summarize, test-summary, │
314
+ │ search/semantic (logic moved to lib), search/stream (logic │
315
+ │ moved into ?stream=true branch). │
316
+ │ Reconciled (M2): trending-topics ← backport v2 features, then │
317
+ │ migrate v2's 3 callers, then deprecate v2. │
318
+ │ KEPT (Codex correction): kg/entities/search (3 KG-UI callers). │
319
+ └──────────────────────────────────────────────────────────────────────┘
320
+
321
+ ┌─────────────────┼─────────────────┐
322
+ ▼ ▼ ▼
323
+ ┌─────────────────────┐ ┌──────────────────┐ ┌─────────────────────┐
324
+ │ POSTGRES │ │ REDIS │ │ RAILWAY WORKERS │
325
+ │ (UNCHANGED) │ │ (extended) │ │ (UNCHANGED) │
326
+ │ │ │ │ │ │
327
+ │ Same 40+ models │ │ Existing: │ │ Same 4 services: │
328
+ │ │ │ TrendingCache │ │ bullmq-worker │
329
+ │ Same materialized │ │ temporal-cache │ │ clustering-worker │
330
+ │ view, same │ │ request_cache │ │ trending-worker │
331
+ │ indexes, same │ │ │ │ scraper-worker │
332
+ │ pgvector │ │ NEW caches: │ │ │
333
+ │ │ │ intent-cache │ │ Same queue topology│
334
+ │ Schema changes: │ │ (LRU 1k, 24h) │ │ Same KG write paths│
335
+ │ ZERO required │ │ intent-prompt │ │ Same self-timer │
336
+ │ │ │ embedding- │ │ disable │
337
+ │ Optional addition: │ │ cache (T2) │ │ │
338
+ │ intent_log table │ │ │ │ │
339
+ │ for telemetry │ │ Same 5-min TTLs │ │ │
340
+ │ (Phase 9, not │ │ │ │ │
341
+ │ blocking) │ │ Same Upstash KV │ │ │
342
+ └─────────────────────┘ └──────────────────┘ └─────────────────────┘
343
+ ```
344
+
345
+ ### 2.2 Architectural principles
346
+
347
+ 1. **One front door** — `/api/search` is the sole search entry. Aliases keep backward-compat during cutover.
348
+ 2. **Cascade for intent, planner for retrieval** — typed intent fans out deterministically to one of three RAG patterns.
349
+ 3. **Format is a first-class field** — table / chart / timeline / graph / unsupported-format are normalized response shapes, not afterthoughts.
350
+ 4. **Synthesis narrates, never fabricates** — when format isn't default, LLM writes 1-paragraph narration; rows/series come from Postgres.
351
+ 5. **Honest downgrade over silent failure** — if user asks for "tornado diagram" and the data can't honestly support it, return `kind:'unsupported-format'` with a reason and a substituted ranked-bar chart.
352
+ 6. **Reversible cutover** — every phase ships behind a feature flag, every alias is keep-able indefinitely.
353
+ 7. **No infra change required** — Postgres schema, Railway topology, Vercel cron, Redis cache layer all stay as-is.
354
+
355
+ ---
356
+
357
+ ## 3. Layer-by-Layer Change Matrix
358
+
359
+ > **Intent of this section:** A scannable accountability table — for every component in the stack, state explicitly whether it changes, moves, gets added, or stays put, with a one-line reason. Anyone asking "does my layer get touched?" should be able to find the answer here in under thirty seconds. Particularly useful for ops/infra reviewers who only care about Postgres / Railway / cron impact (answer: ~none).
360
+
361
+ | Layer | Component | Action | Why |
362
+ |---|---|---|---|
363
+ | **Database (Postgres)** | All 40+ models | ✅ NO CHANGE | Schema is fine; the pain is at the application layer |
364
+ | **Database (Postgres)** | `mv_active_trending_topics` materialized view | ✅ NO CHANGE | Already refreshed every 30min via cron |
365
+ | **Database (Postgres)** | `request_cache` table | ✅ NO CHANGE | Stays as-is; Redis is primary cache |
366
+ | **Database (Postgres)** | NEW: optional `intent_log` table | ➕ OPTIONAL ADD (Phase 9) | Telemetry only; not required for cutover |
367
+ | **Cache (Upstash Redis)** | `lib/redis-connection.ts` | ✅ NO CHANGE | Singleton manager keeps working |
368
+ | **Cache (Upstash Redis)** | `lib/cache/trending-cache.ts` | ✅ NO CHANGE | Used by trending route post-consolidation |
369
+ | **Cache (Upstash Redis)** | `lib/cache/temporal-analysis-cache.ts` | ✅ NO CHANGE | Used by temporal mode |
370
+ | **Cache (Upstash Redis)** | NEW: intent classifier LRU | ➕ ADD | New file `lib/search/intent/intent-cache.ts`. Same Redis instance, 24h TTL |
371
+ | **Cache (Upstash Redis)** | NEW: prototype-embedding cache (T2) | ➕ ADD | One-time precompute, stored under `search:embed:proto:*` |
372
+ | **Railway: bullmq-worker** | Queues entities, relationships, embeddings, summaries | ✅ NO CHANGE | Continues current behavior |
373
+ | **Railway: clustering-worker** | article-clustering + kg-extraction co-located | ✅ NO CHANGE | KG writes flow same path |
374
+ | **Railway: trending-worker** | trending-default queue | ✅ NO CHANGE | Self-timer still disabled; Vercel cron still triggers |
375
+ | **Railway: scraper-worker** | scraper-pipeline (concurrency 20) | ✅ NO CHANGE | RSS ingest unchanged |
376
+ | **Vercel: cron jobs** | All 11 cron entries | ✅ NO CHANGE | Same cadence, same paths |
377
+ | **Vercel: API — search front door** | `/api/intelligent-search` (1582 lines) | 🔄 SHARED-HANDLER SHIM → both routes call the same orchestrator function (no 308; method/headers preserved) | One front door, no client breakage |
378
+ | **Vercel: API — search front door** | `/api/search` (399 lines) | 🔄 REPLACE with new orchestrator | Becomes the entry point |
379
+ | **Vercel: API — intent layer** | inline regex (1170-1181) | ➖ REMOVE | Subsumed by cascade |
380
+ | **Vercel: API — intent layer** | `lib/search/query-router.ts` | 🔄 PROMOTE & EXTEND | Already test-covered; add `format`, `domains` fields |
381
+ | **Vercel: API — planner** | logic embedded in `intelligent-query-engine.ts` | ➡️ EXTRACT to `lib/search/planner.ts` | Pure function, debuggable |
382
+ | **Vercel: API — orphans (rescue)** | `/api/search/semantic` (HyDE) | ➡️ EXTRACT to `lib/search/query-expansion.ts`, delete route | Used inside planner |
383
+ | **Vercel: API — orphans (rescue)** | `/api/search/stream` (SSE) | ➡️ MERGE into `/api/search?stream=true` | Same code path, different transport |
384
+ | **Vercel: API — sibling reconciliation** | `/api/trending-topics-v2` (3 active callers, NOT orphan) | 🔄 Milestone 2: reconcile with `/api/trending-topics` (caller-map first) | Three live trending variants converge after caller map verified |
385
+ | **Vercel: API — orphans (kill)** | `/api/intelligence` | 🔄 PARTIAL: extract dict, delete rest | `Math.random()` violation |
386
+ | **Vercel: API — orphans (kill)** | `/api/ai-summarize` | ➖ DELETE (after cron audit) | Superseded by 8 live summarizers |
387
+ | **Vercel: API — orphans (kill)** | `/api/test-summary` | ➖ DELETE | Test scaffold in production tree |
388
+ | **Vercel: API — KG search** | `/api/kg/entities/search` (3 callers — KGSearchInterface, EntitySearch, EntityRelationshipPanel) | ✅ KEEP both routes; consolidate at lib layer (shared `lib/search/retrieval/entity-search.ts`) | Different consumers, no merge benefit; shared retrieval primitives instead |
389
+ | **Vercel: API — synthesis** | `/api/intelligent-search/summary` | ➡️ MOVE logic into `lib/search/synthesizer.ts`, delete route | Synthesis as library, not endpoint |
390
+ | **Vercel: API — research** | `/api/research/*` | ✅ KEEP | Becomes implementation detail of KG RAG fan-out |
391
+ | **Vercel: API — graph** | `/api/graph/*` | ✅ KEEP | KG visualization & analytics — separate concern |
392
+ | **Vercel: API — trending** | `/api/trending-topics-simple` | ✅ KEEP | 2 callers; not dead. Can deprecate later. |
393
+ | **Frontend: search bars** | `IntelligentSearchBar.tsx`, `V7DSearchBar.tsx`, `V3SearchInput.tsx`, `GlobalSearchBar.tsx`, `SearchModal.tsx`, `SearchModalNew.tsx`, `SearchBottomSheet.tsx`, `ArticleSearchPopout.tsx` | 🔄 CONSOLIDATE into `components/search/SearchBar.tsx` (1) + page-specific wrappers | One source of truth for query handling |
394
+ | **Frontend: results render** | scattered renderers in pages + `PyramidSummary.tsx` | 🔄 CONSOLIDATE into `components/search/SearchResults.tsx` | Format-aware shell |
395
+ | **Frontend: result kinds** | only markdown pyramid today | ➕ ADD `<ResultTable>`, `<ResultChart>`, `<UnsupportedFormat>` | Render the new structured kinds |
396
+ | **Frontend: streaming** | `EventSource` not used anywhere | ➕ ADD `hooks/useSearchStream.ts` | Subscribe to SSE for progressive results |
397
+ | **Frontend: KG search** | `components/graph/EntitySearch.tsx`, `FloatingSearchPill.tsx`, `ResearchEntityDetail.tsx` | ✅ KEEP | KG-specific UI; calls KG RAG via planner under the hood |
398
+ | **Tests** | `tests/lib/search/query-router.test.ts`, `groq-reranker.test.ts`, `rerank-policy.test.ts` | ✅ KEEP & EXTEND | Already covers cascade — extend with format/domains |
399
+ | **Tests** | `tests/unit/intelligent-query-engine-fallback.test.ts` | ✅ KEEP | Fallback path stays |
400
+ | **Eval harness** | `scripts/evaluate-intelligent-search.ts` (Nov 10) | ✅ KEEP & EXTEND | Already exists — extend with intent-eval mode |
401
+ | **Eval harness** | `scripts/create-intelligent-search-datasets.ts` | ✅ KEEP & EXTEND | Add intent labels |
402
+ | **Prompt governance** | inline prompts in routes | ➡️ MOVE to `lib/search/prompts/` | Versioned, scored via `prompt-builder` |
403
+
404
+ **Summary of layer impact:**
405
+
406
+ | Layer | Changes | Risk |
407
+ |---|---|---|
408
+ | Postgres | None (optional intent_log later) | Zero |
409
+ | Redis | Add 2 cache namespaces | Negligible |
410
+ | Railway | None | Zero |
411
+ | Vercel cron | None | Zero |
412
+ | Vercel API | Big surgery — but feature-flagged, aliased, reversible | Medium, contained |
413
+ | Frontend | Big consolidation — but page wrappers preserve URLs | Medium, contained |
414
+
415
+ ---
416
+
417
+ ## 4. File Map — Where Things Are Now → Where They Will Be
418
+
419
+ > **Intent of this section:** A path-by-path translation layer for an implementer. For every relevant file, name the current path, the target path, the purpose, and the action verb (REPLACE / MOVE / RENAME / KEEP / DELETE). When you sit down to write a PR, this is the section you read with the editor open. Sub-tables are organized by concern: front-door, intent cascade, planner, normalization, synthesis, trending, frontend, and "what stays put."
420
+
421
+ ### 4.1 Search-front-door files
422
+
423
+ | Current path | New path | Purpose | Action |
424
+ |---|---|---|---|
425
+ | `app/api/search/route.ts` (399 lines) | `app/api/search/route.ts` | Single search entry, orchestrates cascade → planner → fan-out → normalize → synthesize | REPLACE contents (calls `runSearch()` from lib) |
426
+ | `app/api/intelligent-search/route.ts` (1582 lines) | (shared-handler shim) | Calls the same `runSearch()` function. No 308. Method/headers preserved. Identical response shape. | REPLACE body with `export const POST = runSearch` |
427
+ | `app/api/intelligent-search/summary/route.ts` (1082 lines) | `lib/search/synthesizer.ts` | Library function called from front door | MOVE logic, delete route |
428
+ | `app/api/intelligent-search/health/route.ts` | KEEP at current path; add `/api/search/health` that calls same handler | Liveness check on both URLs | DUAL-MOUNT |
429
+ | `app/api/intelligent-search/cache/clear/route.ts` | KEEP; add `/api/search/cache/clear` that calls same handler | Admin cache invalidation | DUAL-MOUNT |
430
+
431
+ ### 4.2 Intent cascade (NEW — built from existing pieces)
432
+
433
+ | Source | New path | Purpose |
434
+ |---|---|---|
435
+ | Inline regex in `intelligent-search/route.ts:1170-1181` + `lib/search/query-router.ts:performBasicAnalysis` | `lib/search/intent/tier1-regex.ts` | Fast-path regex matcher with LRU cache |
436
+ | (NEW) — uses existing OpenAI embedding model | `lib/search/intent/tier2-embed.ts` | Cosine match against ~30 prototype-intent embeddings (cached in Redis) |
437
+ | `lib/search/query-router.ts:performEnhancedAnalysis` | `lib/search/intent/tier3-llm.ts` | Structured-output call (Groq Llama-3.1-8B primary, Haiku 4.5 fallback) |
438
+ | (NEW) | `lib/search/intent/intent-schema.ts` | Zod schema for the typed `Intent` object |
439
+ | (NEW) | `lib/search/intent/intent-cache.ts` | LRU + Redis cache wrapper |
440
+ | (NEW) | `lib/search/intent/index.ts` | `classifyIntent(query): Promise<Intent>` orchestrator |
441
+
442
+ ### 4.3 Planner & retrieval
443
+
444
+ | Source | New path | Purpose |
445
+ |---|---|---|
446
+ | Logic embedded in `lib/knowledge-graph/intelligent-query-engine.ts` | `lib/search/planner.ts` | Pure function: `planRetrieval(intent) → RetrievalPlan` |
447
+ | `lib/knowledge-graph/intelligent-query-engine.ts` (8 DB tables, ~3500 lines) | `lib/search/retrieval/pipeline-rag.ts` (article + summary fast-path) + `lib/search/retrieval/agentic-rag.ts` (multi-source merge) + `lib/search/retrieval/kg-rag.ts` (entities + papers + relationships) | Decompose by RAG pattern |
448
+ | `app/api/search/semantic/route.ts:16-48` (HyDE expansion) | `lib/search/query-expansion.ts` | Used by retrieval when query is vague |
449
+ | `app/api/intelligence/route.ts:101-112` (trend keywords) | `lib/search/trend-keywords.ts` | Tier 1 regex seeds |
450
+ | `app/api/intelligence/route.ts:67-78` (sentiment regex) | `lib/search/sentiment-regex.ts` | Optional metadata enrichment |
451
+
452
+ ### 4.4 Normalization & response shape
453
+
454
+ | Source | New path | Purpose |
455
+ |---|---|---|
456
+ | (NEW) | `lib/search/normalize.ts` | Build `StructuredResult[]` from retrieval output + intent |
457
+ | (NEW) | `lib/search/types.ts` | TS types for `Intent`, `StructuredResult`, `SearchResponse` |
458
+
459
+ ### 4.5 Synthesis & prompts
460
+
461
+ | Source | New path | Purpose |
462
+ |---|---|---|
463
+ | Inline in `app/api/intelligent-search/summary/route.ts` | `lib/search/synthesizer.ts` | Format-aware narration |
464
+ | Inline prompt strings | `lib/search/prompts/synthesis-default.md` | Pyramid synthesis (current behavior) |
465
+ | (NEW) | `lib/search/prompts/synthesis-with-tables.md` | 1-paragraph narration when tables/charts present |
466
+ | `app/api/search/semantic/route.ts:33-34` (HyDE prompt) | `lib/search/prompts/hyde-expansion.md` | Query expansion |
467
+ | (NEW) | `lib/search/prompts/intent-classifier.md` | Tier 3 classifier prompt |
468
+ | All prompts | scored & versioned via `prompt-builder` library | Governance |
469
+
470
+ ### 4.6 Trending consolidation
471
+
472
+ **Three live variants** — caller map verified 2026-04-28 via template-literal-aware grep:
473
+
474
+ | Current | Active callers | Action (Milestone 2) |
475
+ |---|---|---|
476
+ | `app/api/trending-topics/route.ts` (203 lines, base mode) | live (TBD precise count) | KEEP path; receives v2 features via backport |
477
+ | `app/api/trending-topics-v2/route.ts` (996 lines, multi-mode + MV + quality tiering) | 3 callers: `TrendingTopics.tsx:121`, `TrendingTopicsEnhanced.tsx:422`, `useTrendingTopicsCache.ts:256` | KEEP path during reconciliation; deprecate AFTER consumers migrate |
478
+ | `app/api/trending-topics-simple/route.ts` | 2 callers | KEEP for one release, then deprecate |
479
+
480
+ **Reconciliation strategy** (Milestone 2, 1.5 days):
481
+ 1. Compare response shapes field-by-field across all three.
482
+ 2. Backport v2's missing features (materialized view fast-path, three-mode dispatch, quality tier ranking, domain filter, dedup) into `/api/trending-topics`.
483
+ 3. Migrate v2 consumers one at a time to base path.
484
+ 4. Only after all three callers move: delete v2.
485
+
486
+ ### 4.7 Frontend consolidation
487
+
488
+ | Current paths (8+ search bars) | New path | Action |
489
+ |---|---|---|
490
+ | `components/IntelligentSearchBar.tsx` | `components/search/SearchBar.tsx` | One canonical bar |
491
+ | `components/V7DSearchBar.tsx` | (deleted, V7DSearchPage uses SearchBar) | DELETE after migration |
492
+ | `components/v3/V3SearchInput.tsx` | (deleted) | DELETE |
493
+ | `components/v3/GlobalSearchBar.tsx` | (page-specific wrapper around SearchBar) | REPLACE internals |
494
+ | `components/SearchModal.tsx`, `SearchModalNew.tsx` | merge into `components/search/SearchModal.tsx` | KEEP one |
495
+ | `components/SearchBottomSheet.tsx` | `components/search/SearchBottomSheet.tsx` (mobile shell over SearchBar) | RENAME |
496
+ | `components/ArticleSearchPopout.tsx` | (page-specific wrapper) | KEEP, retarget |
497
+ | `components/v3/SearchOverlay.tsx`, `SearchOverlayProvider.tsx` | `components/search/SearchOverlay.tsx` | RENAME |
498
+ | `components/graph/FloatingSearchPill.tsx`, `EntitySearch.tsx` | (KG-specific, KEEP) | UNCHANGED — they call KG RAG via planner |
499
+
500
+ | New files | Purpose |
501
+ |---|---|
502
+ | `components/search/SearchResults.tsx` | Format-aware result shell |
503
+ | `components/search/ResultTable.tsx` | Sortable table with sticky first column, mono dates |
504
+ | `components/search/ResultChart.tsx` | Recharts wrapper for bar / ranked-bar / timeline / line / "tornado" |
505
+ | `components/search/ResultGraph.tsx` | KG node-link viz (extract from existing graph code) |
506
+ | `components/search/UnsupportedFormat.tsx` | Honest downgrade notice |
507
+ | `hooks/useSearch.ts` | One-shot POST flow |
508
+ | `hooks/useSearchStream.ts` | SSE EventSource flow |
509
+
510
+ ### 4.8 What stays put (touched but not moved)
511
+
512
+ | File | Why |
513
+ |---|---|
514
+ | `lib/redis-connection.ts` | Singleton — works as-is |
515
+ | `lib/cache/trending-cache.ts`, `temporal-analysis-cache.ts` | Used by consolidated trending route |
516
+ | `lib/prisma.ts` + `lib/prisma-cached-queries.ts` | DB layer untouched |
517
+ | `lib/ai/groq-service.ts`, `lib/ai/unified-ai-service.ts`, `lib/ai/fact-extraction-service.ts` | Provider abstractions stay |
518
+ | `prisma/schema.prisma` | No schema changes required |
519
+ | `vercel.json` | Cron entries unchanged |
520
+ | `ecosystem.config.js`, Railway nixpacks config | Worker topology unchanged |
521
+ | `tests/lib/search/*` | Extended, not rewritten |
522
+ | All `/api/research/*`, `/api/graph/*`, `/api/cron/*` routes | Still used as KG/cron implementation detail |
523
+
524
+ ---
525
+
526
+ ## 5. Step-by-Step Execution Plan (10 Phases — Two Milestones)
527
+
528
+ > **Intent of this section:** The work order. Every phase has a clear deliverable, a flag, and a reversal path. The two-milestone split is the single most important thing here: Milestone 1 ships the bug fix without touching anything live; Milestone 2 does the cleanups only after M1 has soaked. If you are starting work, you start at Phase 0. If you are reviewing, check that no phase in M2 leaks into M1.
529
+
530
+ Every phase ships independently. Every phase has a flag. Every phase is reversible. Milestone 1 fixes the visible bug end-to-end without deleting or moving anything live.
531
+
532
+ ```
533
+ ═══════════ MILESTONE 1 — Format-intent + structured results + retrieval lift ═══
534
+ (mergeable alone, ~8 days · v2.2 SOTA additions)
535
+
536
+ PHASE 0 Eval harness — extend + add RAGAS metrics ─── 0.5 day (was 0.25)
537
+
538
+ PHASE 1 Intent cascade T1+T3 (shadow mode) ─── 1.0 day
539
+ │ Tier 3 = Groq openai/gpt-oss-120b strict mode
540
+
541
+ PHASE 2 Intent schema extension ─── 0.25 day
542
+
543
+ PHASE 3 Structured-result kinds + UI ─── 1.0 day
544
+ │ (table, chart, unsupported-format)
545
+
546
+ PHASE 4 Planner + RRF + cross-encoder placeholder ─── 3.0 days (was 1.5)
547
+ │ + CRAG self-correction gate
548
+ │ (LLM-rerank stays via Groq Llama-3.3-70b;
549
+ │ cross-encoder swap deferred to Phase 11)
550
+
551
+ PHASE 5 Shared-handler orchestrator behind flag ─── 0.5 day
552
+
553
+ PHASE 8a Synthesis prompts — default + with-tables ─── 0.25 day
554
+
555
+ PHASE 9a Telemetry — log every classification ─── 0.25 day
556
+ ───────────
557
+ MILESTONE 1 TOTAL: ~8 days
558
+
559
+ ────────── soak 1-2 weeks · monitor disagreements · validate UX ────────
560
+
561
+ ═══════════ MILESTONE 2 — Consolidations + KG quality + adaptive ═══════
562
+ (defer until M1 soaked, ~6 days · v2.2 additions)
563
+
564
+ PHASE 5b Multi-query (primary) + HyDE (vague fallback) ─── 1.5 days (was 0.5)
565
+
566
+ PHASE 5c RECOVER SSE progressive search ─── 1.0 day
567
+
568
+ PHASE 5d Trending v1↔v2 reconciliation ─── 1.5 days
569
+
570
+ PHASE 5e HippoRAG-style PPR over entity_pairs ─── 1.5 days (NEW v2.2)
571
+
572
+ PHASE 6 Delete confirmed orphans ─── 0.25 day
573
+
574
+ PHASE 6b Extract trend dict + sentiment regex ─── 0.25 day
575
+
576
+ PHASE 7 Tier 2 embedding classifier ─── 0.5 day
577
+
578
+ PHASE 8b Full prompt-builder governance pass ─── 0.25 day
579
+
580
+ PHASE 9b Full telemetry + intent_log + PII policy ─── 0.25 day
581
+
582
+ PHASE 10 Adaptive complexity routing + RAGAS gate ─── 0.5 day (NEW v2.2)
583
+ ───────────
584
+ MILESTONE 2 TOTAL: ~6 days
585
+
586
+ ────────── soak again · measure faithfulness/recall/precision ──────────
587
+
588
+ ═══════════ MILESTONE 3 — Model swaps (LAST STEP, ~1 day) ══════════════
589
+ (only after end-to-end works, per user direction)
590
+
591
+ PHASE 11 Evaluate + swap candidates ─── ~1 day (NEW v2.2)
592
+ · Cross-encoder reranker (Jina v3 / Voyage 2.5
593
+ / Zerank-2 / BGE-self-host)
594
+ · OpenAI Structured Outputs for Tier 3 (only
595
+ if Groq strict mode shows drift in telemetry)
596
+ · ColBERT/PLAID late interaction (only if recall
597
+ plateaus < 90%)
598
+ ───────────
599
+ MILESTONE 3 TOTAL: ~1 day
600
+ ```
601
+
602
+ **Total focused engineering: ~14 days** (was ~9d in v2.1) across 4-5 calendar weeks with shadow-mode soak between milestones. The +5 days buys: ~84-91% recall@10 (from ~62-78%), agentic self-correction, multi-hop graph retrieval, and a clean off-ramp to dedicated rerankers without committing upfront.
603
+
604
+ ### 5.1 Phase 0 — Extend the existing eval harness + RAGAS metrics (0.5 day, +0.25d in v2.2)
605
+
606
+ **Goal:** Without an eval, every later change is blind. v2.2 expands eval to retrieval+synthesis quality, not just intent accuracy.
607
+
608
+ - Open `scripts/evaluate-intelligent-search.ts` and `scripts/create-intelligent-search-datasets.ts`.
609
+ - Add `intent` labels to the dataset schema: `domains`, `format`, `chartSubtype`, `horizonDays`, **`complexity`** (new in v2.2).
610
+ - Hand-label **200 queries** (was ~50 in v2.1 — corrected for §6 verification consistency) that exercise format requests, multi-domain ("research and releases"), ambiguous cases, simple/medium/complex complexity tiers, and known multi-hop queries.
611
+ - Set **90% per-field intent accuracy** as the merge gate for Phase 1+.
612
+ - **NEW (v2.2): Add RAGAS-style reference-free metrics** judged by Groq Llama 3.3 70B (cheap, ~$5/full run):
613
+ - **Faithfulness** ≥ 0.85 — no claim in synthesis without grounding in retrieved context.
614
+ - **Context Recall** ≥ 0.80 — retrieval brought in the docs needed.
615
+ - **Context Precision** ≥ 0.75 — retrieved docs were relevant, not noise.
616
+ - **Answer Relevance** ≥ 0.85 — synthesis actually answered the question.
617
+ - These are merge gates for any phase that touches retrieval or synthesis (4, 5b, 5e, 8a, 11).
618
+
619
+ **Reversible?** N/A — additive only.
620
+
621
+ ### 5.2 Phase 1 — Intent cascade T1+T3 (shadow mode) (1 day)
622
+
623
+ **Goal:** Stand up the cascade as a parallel data-collection layer. Don't act on output yet. **Stay on Groq throughout** — pick the right Groq model per tier.
624
+
625
+ - Create `lib/search/intent/intent-schema.ts` with the Zod `Intent` type. Mirror it as a JSON Schema literal for Groq's strict `response_format`.
626
+ - Create `lib/search/intent/tier1-regex.ts` — promote and rename existing regex rules from `intelligent-search/route.ts:1170-1181` and `query-router.ts:performBasicAnalysis`. Add format/domain detection.
627
+ - Create `lib/search/intent/tier3-llm.ts` — **Groq-first, accuracy-correct model selection**:
628
+ - **Primary: Groq `openai/gpt-oss-120b` with `response_format: { type: 'json_schema', strict: true, json_schema: IntentJsonSchema }`.** Constrained decoding → 100% schema-valid by construction. ~500 tps, $0.15/$0.75 per 1M tok. This is the *correct* Groq model for this job, not Llama 3.1 8B (which only supports `json_object` and would let the typed `Intent` drift on field/enum errors).
629
+ - **Fallback A: Groq `openai/gpt-oss-20b` with strict `json_schema`** — same constrained-decoding guarantee, smaller/cheaper, available on the same provider. Use on circuit-breaker open or 120b unavailable.
630
+ - **Fallback B: Groq `meta-llama/llama-4-scout-17b-16e-instruct` with best-effort `json_schema`** — schema-aware but not strict; validate with Zod and re-prompt once on parse failure.
631
+ - **Final fallback: Groq `llama-3.1-8b-instant` with `json_object`** + Zod validation + 1 retry. Last resort; still cheaper than any OpenAI/Anthropic call.
632
+ - **Why this is accuracy-first AND speed/cost-optimized** — verified at console.groq.com/docs/structured-outputs (2026-04-28): only `openai/gpt-oss-{20b,120b}` use constrained decoding for guaranteed schema adherence on Groq. Llama models on Groq only enforce "valid JSON" via `json_object` mode, which can drift on field names, enum values, and array shapes. Picking gpt-oss-120b keeps you on Groq (cheaper/faster than OpenAI Structured Outputs) AND keeps the typed `Intent` schema-strict (the shape gates planner + normalize + synthesize downstream).
633
+ - **Cross-provider escape hatch (deferred)** — if production telemetry shows gpt-oss-120b strict-mode is misclassifying despite schema-validity, the `lib/search/intent/tier3-llm.ts` provider abstraction makes swapping to OpenAI `gpt-4.1-mini` Structured Outputs a one-line change. Defer the swap until the architecture is working end-to-end and there is data demanding it.
634
+ - Create `lib/search/intent/intent-cache.ts` — Redis-backed LRU keyed on raw query, 24h TTL. Cache key includes `IntentSchema.version` so schema bumps invalidate automatically.
635
+ - Create `lib/search/intent/index.ts` — `classifyIntent(query)` orchestrator.
636
+ - Wire `classifyIntent()` into `/api/intelligent-search/route.ts` in **shadow mode** behind `ENABLE_INTENT_CASCADE_SHADOW=true`. Log results, don't act on them.
637
+ - Run for 24-48h, compare classifier output to current regex output. **Track schema-validation failure rate per model** as a quality signal even though gpt-oss-120b strict mode should be ~0%.
638
+
639
+ **Reversible?** Yes — flag to off.
640
+
641
+ ### 5.3 Phase 2 — Intent schema extension (0.25 day)
642
+
643
+ - Extend `Intent` type with `format`, `chartSubtype`, `domains`. Already done in Phase 1's schema.
644
+ - Update existing `query-router.ts` consumers to read these fields when present. Default behavior unchanged when fields absent.
645
+ - Add unit tests in `tests/lib/search/intent/`.
646
+
647
+ **Reversible?** Yes — typed extension, default values mean old callers still work.
648
+
649
+ ### 5.4 Phase 3 — Structured-result kinds + UI (1 day)
650
+
651
+ - Create `lib/search/types.ts` with `StructuredResult` discriminated union.
652
+ - Create `components/search/ResultTable.tsx`, `ResultChart.tsx`, `UnsupportedFormat.tsx`.
653
+ - Create `components/search/SearchResults.tsx` shell.
654
+ - Verify Recharts is already in `package.json` (it likely is — `app/chart-sandbox/page.tsx` exists).
655
+ - Keep `<PyramidSummary />` rendering when `format === 'default'` — no regression.
656
+
657
+ **Reversible?** Yes — new components, old code path untouched.
658
+
659
+ ### 5.5 Phase 4 — Planner extraction + multi-domain fan-out + RRF + CRAG gate (3 days)
660
+
661
+ **Up from 1.5d in v2.1 — adds two SOTA-validated retrieval improvements that are pure code changes (no new model dependency, no provider swap).**
662
+
663
+ - Create `lib/search/planner.ts` — pure function `planRetrieval(intent) → RetrievalPlan`.
664
+ - Carefully extract retrieval primitives from `lib/knowledge-graph/intelligent-query-engine.ts` into:
665
+ - `lib/search/retrieval/pipeline-rag.ts` (articles + summaries; the common case)
666
+ - `lib/search/retrieval/agentic-rag.ts` (multi-source fan-out + merge)
667
+ - `lib/search/retrieval/kg-rag.ts` (entities, papers, relationships — calls `/api/research/*` and `/api/graph/*` internally)
668
+
669
+ **NEW (v2.2) — Reciprocal Rank Fusion (RRF) inside `pipeline-rag.ts`** (~1 day):
670
+ - Today's pipeline runs vector search (pgvector) and keyword search (`title ILIKE`) in sequence with implicit precedence. Replace with parallel queries + RRF fusion.
671
+ - Run pgvector `ORDER BY embedding <-> $query LIMIT 50` AND Postgres `tsvector` (`websearch_to_tsquery`) `LIMIT 50` in parallel.
672
+ - Fuse with RRF: `score(doc) = Σ 1/(k + rank_i)` across both lists, k=60. Trim to top-20 for rerank.
673
+ - Recall lift expectation: ~62–78% → ~84–91% recall@10 (per Supermemory, ParadeDB, Tiger Data, Weaviate benchmarks). **Verify on example-app fixtures via Phase 0 eval — direction is documented; magnitude must be measured.**
674
+ - Keep current Groq Llama 3.3 70B as the reranker over the top-20 fused candidates. Dedicated cross-encoder reranker swap is deferred to **Phase 11 (last step)** per user direction — code is wired so the swap is one provider-abstraction change.
675
+
676
+ **NEW (v2.2) — CRAG-style relevance gate inside `agentic-rag.ts`** (~0.5 day):
677
+ - After rerank, compute average relevance score across top-N reranked docs.
678
+ - If `avg_relevance < 0.5` (tunable from telemetry):
679
+ - Branch A: trigger one query reformulation pass via existing Groq Llama 3.3 70B (rewrite, retry retrieval once).
680
+ - Branch B: if Branch A also fails the threshold, return `{ kind: 'low-confidence', reason, fallback: <broader-query results> }` as an honest-downgrade structured result — same pattern the plan already uses for `unsupported-format`.
681
+ - Log every gate trigger; tune threshold from one week of shadow-mode data.
682
+ - This makes the "agentic" branch *actually* agentic (critique-and-retry), not just fan-out-and-merge.
683
+
684
+ - Add a `?intent_v2=true` flag to `/api/intelligent-search` that routes through the new planner. Default off.
685
+
686
+ **Reversible?** Yes — flag to off. RRF is additive (old code path stays); CRAG gate can be disabled by env var.
687
+
688
+ ### 5.6 Phase 5 — Promote `/api/search` as front door via SHARED HANDLER (0.5 day)
689
+
690
+ **Rationale (Codex review):** A 308 redirect changes the URL the client observes, can break method/header preservation in older clients, and is hard to revert mid-request. A shared handler keeps both routes mounted with identical behavior — zero client surface change.
691
+
692
+ - Create `lib/search/runSearch.ts` exporting `async function runSearch(req: Request): Promise<Response>` — the new orchestrator (`classifyIntent → planRetrieval → fanOutRetrieve → normalize → synthesize → respond`).
693
+ - Replace contents of `app/api/search/route.ts` with `export const POST = runSearch`.
694
+ - Replace contents of `app/api/intelligent-search/route.ts` with `export const POST = runSearch`. Same handler, two URLs, identical response.
695
+ - Move `/api/intelligent-search/summary/route.ts` logic into `lib/search/synthesizer.ts`. Delete the route file (no external callers — synthesis was internal).
696
+ - Dual-mount `/api/search/health` and `/api/search/cache/clear` alongside the originals.
697
+
698
+ **Reversible?** Yes — `runSearch` runs behind `ENABLE_SEARCH_ORCHESTRATOR_V2=true`. Flag off → both routes fall back to the old `intelligent-search` body. No URL change, no client work.
699
+
700
+ ### 5.7 Phase 5b — Query rewriting: multi-query primary + HyDE fallback (1.5 days, +1d in v2.2)
701
+
702
+ **v2.2 update:** DMQR-RAG (multi-query rewriting) shows +14.46% P@5 on FreshQA and +8% on HotpotQA multi-hop vs HyDE-only. RAG-Fusion (multi-query + RRF) is the 2026 standard. HyDE remains valuable specifically for *vague* queries; multi-query better for *complex/multi-hop*. Plan now does both, routed by intent.
703
+
704
+ - Extract `expandQuery()` from `app/api/search/semantic/route.ts:16-48` into `lib/search/query-expansion.ts`.
705
+ - Move the HyDE prompt to `lib/search/prompts/hyde-expansion.md`.
706
+ - **NEW: Add multi-query rewriter** (`lib/search/query-multiquery.ts`):
707
+ - Generate 3 alternative phrasings via Groq Llama 3.3 70B (existing model, no swap).
708
+ - Run RRF fusion across all 4 candidate lists (original + 3 rewrites).
709
+ - Cap rewrites at 3; gate on `intent.complexity ≥ medium` (see Phase 2 complexity field below) to avoid cost on simple queries.
710
+ - **Routing rule (in planner)**:
711
+ - `intent.complexity = 'simple'` → no rewrite (skip both).
712
+ - `intent.complexity = 'medium'` AND `tokenCount ≤ 4` → HyDE only (vague short query).
713
+ - `intent.complexity = 'complex'` → multi-query (RAG-Fusion).
714
+ - `intent.complexity = 'complex'` AND vague → both, fused.
715
+ - Add `complexity: 'simple' | 'medium' | 'complex'` to `Intent` schema (cheap heuristic in Tier 1: token count + entity count + presence of "vs/compare/why/how" patterns). This is the Adaptive RAG pattern from §13.
716
+ - Run all expansion prompts through `prompt-builder:score`; optimize if < 80.
717
+
718
+ **Reversible?** Yes — flag `ENABLE_QUERY_REWRITE=false` (covers both). Sub-flags `ENABLE_HYDE` / `ENABLE_MULTIQUERY` for granular rollback.
719
+
720
+ ### 5.8 Phase 5c — Recover SSE progressive search (1 day)
721
+
722
+ - Add `?stream=true` branch to `/api/search/route.ts`. When set, return SSE; otherwise JSON.
723
+ - Reuse the three-phase pattern from `app/api/search/stream/route.ts:24-181` — keyword → vector → expansion.
724
+ - Create `hooks/useSearchStream.ts` with `EventSource` wrapper.
725
+ - Add `streamMode={true}` prop to `<SearchBar />` for pages that opt in (start with `/search`).
726
+
727
+ **Reversible?** Yes — frontend prop default `false`.
728
+
729
+ ### 5.9 Phase 5d — Trending v1↔v2 consolidation (1.5 days)
730
+
731
+ - Compare field-by-field: components consuming `/api/trending-topics` (live, base mode), `/api/trending-topics-simple` (live, 2 callers), `/api/trending-topics-v2` (live, 3 callers — multi-mode + MV + quality tiering).
732
+ - Backport v2's materialized-view fast-path, three-mode dispatch, quality tier ranking (VERIFIED/EMERGING/SPECULATIVE), domain filter, deduplication into `/api/trending-topics`.
733
+ - Verify `?mode=default|temporal|thematic` works on the consolidated route.
734
+ - Migrate consumers one-by-one to base path: `useTrendingTopicsCache.ts:256`, `TrendingTopics.tsx:121`, `TrendingTopicsEnhanced.tsx:422`. Smoke-test each.
735
+ - Only after all three callers migrated: delete `/api/trending-topics-v2`.
736
+
737
+ **Reversible?** Soft — restorable from git, but consumers will have moved by then.
738
+
739
+ ### 5.9.5 Phase 5e — HippoRAG-style PPR over `entity_pairs` for KG-RAG (1.5 days, NEW in v2.2)
740
+
741
+ **Why:** GraphRAG-Bench (ICLR'26) shows HippoRAG / HippoRAG2 lead on multi-hop reasoning — Evidence Recall 87–91%, Context Relevance 85–88% — vs Microsoft GraphRAG (community-summary, expensive to index) and LightRAG (lower latency, lower accuracy). example-app's `entities` + `entity_pairs` schema fits HippoRAG's Personalized PageRank pattern *natively* — no new tables, no re-indexing.
742
+
743
+ - In `lib/search/retrieval/kg-rag.ts`: replace the current "fetch entity neighbors" walk with a Personalized PageRank seeded by entities matched in the query.
744
+ - Implementation: precompute entity_pair edge weights from co-occurrence + recency; run PPR (5–10 iterations, damping 0.85) over the seeded entities; rank papers/articles by their entity-membership score.
745
+ - Cache PPR vectors in Redis keyed on `seed_entity_set`; 6h TTL since `entity_pairs` updates daily via clustering-worker.
746
+ - Verify schema indices on `entity_pairs(source_id, target_id)` and `entity_pairs(target_id, source_id)` exist before deploying — PPR over 1M+ edge graphs needs them.
747
+ - Keep current KG retrieval as the fallback path (`USE_HIPPORAG=false`).
748
+
749
+ **Reversible?** Yes — flag-gated. Old KG path stays as-is.
750
+
751
+ ### 5.10 Phase 6 — Delete confirmed orphans (Milestone 2, 0.25 day)
752
+
753
+ **Rule:** Each delete preceded by a **template-literal-aware grep** (NOT just NavGator's `frontend-calls-api` connection check, which misses `\`/api/${var}\``) AND a `npx navgator impact <stable-id>` check.
754
+
755
+ ```bash
756
+ # template-literal-aware grep recipe
757
+ grep -rnE "['\"\`]/api/<route-name>['\"\`]?" --include='*.ts*' app components hooks lib
758
+ grep -rnE "/api/<route-name>" --include='*.ts*' app components hooks lib # broader catch
759
+ ```
760
+
761
+ Confirmed-deletable (verified 2026-04-28):
762
+ - `app/api/intelligence/route.ts` — 0 callers via both grep patterns. Delete after dict extract (Phase 6b).
763
+ - `app/api/ai-summarize/route.ts` — 0 callers; delete after cron audit (`grep -E "ai-summarize" vercel.json`).
764
+ - `app/api/test-summary/route.ts` — 0 callers, test scaffold.
765
+ - `app/api/search/semantic/route.ts` — after Phase 5b extracts HyDE logic.
766
+ - `app/api/search/stream/route.ts` — after Phase 5c merges SSE logic.
767
+
768
+ **Removed from delete list (Codex correction):**
769
+ - ❌ ~~`app/api/kg/entities/search/route.ts`~~ — KEEP, 3 KG-UI callers (`KGSearchInterface.tsx:103`, `EntitySearch.tsx:127`, `EntityRelationshipPanel.tsx:412`).
770
+ - ❌ ~~`app/api/trending-topics-v2/route.ts`~~ — KEEP, 3 callers (`TrendingTopics.tsx:121`, `TrendingTopicsEnhanced.tsx:422`, `useTrendingTopicsCache.ts:256`); reconcile in Phase 5d.
771
+
772
+ **Reversible?** Soft — git revert.
773
+
774
+ ### 5.11 Phase 6b — Extract trend dict + sentiment regex (0.25 day)
775
+
776
+ - `lib/search/trend-keywords.ts` ← from `intelligence/route.ts:101-112`
777
+ - `lib/search/sentiment-regex.ts` ← from `intelligence/route.ts:67-78`
778
+ - Wire into Tier 1 regex matcher (intent cascade) and optional metadata enrichment in normalize.
779
+
780
+ ### 5.12 Phase 7 — Tier 2 embedding classifier (0.5 day)
781
+
782
+ - Pre-compute embeddings for ~30 prototype intents ("show me a table", "compare X vs Y", "trending in research", "tornado diagram of news"). Store in Redis under `search:embed:proto:*`.
783
+ - Cosine match incoming query embedding (reuse existing OpenAI embedding model) against prototypes.
784
+ - Insert between Tier 1 and Tier 3.
785
+ - Rebalance: target ~70/20/10 traffic at T1/T2/T3.
786
+
787
+ **Reversible?** Yes — flag to bypass T2.
788
+
789
+ ### 5.13 Phase 8 — Synthesizer prompt update + governance (0.5 day)
790
+
791
+ - Two prompts in `lib/search/prompts/`:
792
+ - `synthesis-default.md` (current pyramid behavior)
793
+ - `synthesis-with-tables.md` (1-paragraph narration only)
794
+ - Synthesizer reads from these files at startup; hot-swap via env var `SYNTHESIS_PROMPT_VERSION=2026-04-30`.
795
+ - Run all three prompts (HyDE, intent classifier, synthesis) through `prompt-builder:score` → `prompt-builder:optimize` if any < 80.
796
+ - Save final versions via `prompt-builder:save` to project library.
797
+
798
+ ### 5.14 Phase 9 — Telemetry + weekly review (continuous)
799
+
800
+ - Log every classification: `query`, `intent`, `confidence`, `tier`, `latencyMs`, `model`. Sample 5% to a review queue.
801
+ - Optional new table `intent_log` in Postgres if you want SQL-queryable history (not blocking).
802
+ - Weekly: review disagreements between regex fast-path and LLM classifier, retune Tier 1 rules.
803
+ - Cost dashboard: alert if classifier spend > $1/day at current traffic.
804
+ - **PII policy (v2.2):** queries can carry PII (emails, names, internal project codenames). Apply hash-only-store for the 5% review sample, OR opt-in flag for full-query storage, OR redact via regex pre-store. Decide before Phase 9 ships.
805
+
806
+ ### 5.15 Phase 10 — Adaptive complexity routing + RAGAS-style eval (0.5 day, NEW in v2.2)
807
+
808
+ - Wire the `intent.complexity` field (added in Phase 5b) into the planner: `simple` → skip retrieval where possible (cache hit + return), `medium` → standard RAG, `complex` → multi-step + multi-query.
809
+ - Extend `scripts/evaluate-intelligent-search.ts` with four RAGAS-style reference-free metrics, judged by Groq Llama 3.3 70B (cheap, ~$5/full eval run):
810
+ - **Faithfulness** — every claim in the response is grounded in retrieved context.
811
+ - **Context Recall** — the retrieval brought in the documents needed to answer.
812
+ - **Context Precision** — retrieved docs are relevant (not noise).
813
+ - **Answer Relevance** — the response answers the asked question.
814
+ - Set merge gate: no phase ships unless faithfulness ≥ 0.85 AND context recall ≥ 0.80 AND answer relevance ≥ 0.85 vs the v2.1 baseline.
815
+
816
+ ### 5.16 Phase 11 — Deferred model swaps (LAST STEP, only after end-to-end works) (~1 day)
817
+
818
+ **Per user direction (2026-04-28):** new-LLM swaps come last, after the architecture is proven working. Each swap is a one-line provider-abstraction change because Phase 1 / Phase 4 / Phase 5b already wired the abstraction.
819
+
820
+ Candidates to evaluate at this phase, in priority order:
821
+
822
+ 1. **Dedicated cross-encoder reranker** (highest expected lift). Replace LLM-rerank-via-Groq-70B with one of:
823
+ - **Jina Reranker v3** — 81.33% Hit@1 at 188ms (sub-200ms champion); managed API, listwise, 131k context.
824
+ - **Voyage Rerank 2.5** — competitive accuracy, ~2× lower latency than Cohere.
825
+ - **Zerank-2** — current ELO leader (1638) on 2026 reranker leaderboards.
826
+ - **BGE-reranker-v2-m3** — open-source, self-host on existing Railway worker fleet (matches user's "build from scratch" preference).
827
+ - Decision criterion: run all four against example-app fixtures via Phase 0 eval; pick the model with the best (faithfulness × context-recall) / latency curve. Expected latency win: 500–2000ms LLM-rerank → 50–200ms cross-encoder.
828
+ 2. **OpenAI Structured Outputs for Tier 3** — only if Groq `openai/gpt-oss-120b` strict mode shows ≥3% schema-validation drift in production telemetry. Drop-in `gpt-4.1-mini` or `gpt-5-mini` if needed.
829
+ 3. **Cohere Rerank 4.0 Pro / Fast** — comparable accuracy to top-tier; managed; only if self-host BGE creates ops burden you don't want.
830
+ 4. **Late interaction (ColBERT v2 / PLAID)** — premium upgrade after RRF caps out; only if recall@10 plateaus below 90% on example-app-specific eval.
831
+
832
+ **Reversible?** Yes — provider abstraction means each swap is config-only.
833
+
834
+ ---
835
+
836
+ ## 6. Verification
837
+
838
+ > **Intent of this section:** Define "done" before any code is written. Each row names a test and what passing it actually proves about the system. The two failing user queries appear here as **Integration A and B** — they are the canonical proof that the bug is fixed. If a phase gets shipped without its row turning green, treat it as not shipped.
839
+
840
+ | Test | What it proves |
841
+ |---|---|
842
+ | **Unit** — 200 labeled fixture set, ≥ 90% per-field intent accuracy | Cascade is honest about what it understands |
843
+ | **Integration A** — re-run "What's the latest AI trends for research and product releases. share this in a table" | Response contains `structuredResults: [{kind:'table'}, …]`, prose ≤ 1 paragraph |
844
+ | **Integration B** — re-run "create a tornado diagram of recent news trends" | Response contains `kind:'unsupported-format'` with substituted ranked-bar `kind:'chart'` |
845
+ | **Integration C** — query "what's trending in AI research and product launches?" | Multi-domain fan-out: response contains release rows AND paper rows from KG RAG, not just news articles |
846
+ | **Smoke** — old URL `/api/intelligent-search` still returns valid response | Alias works |
847
+ | **Smoke** — SSE: subscribe to `/api/search?stream=true` | Receive keyword phase < 200ms, vector phase < 1s |
848
+ | **Performance** — Tier 1 hit latency p99 | < 25ms |
849
+ | **Performance** — full request latency (T3 + retrieval + synthesis) p95 | < 1500ms |
850
+ | **Cost** — daily classifier spend dashboard | < $1/day at current traffic |
851
+ | **NavGator** — `npx navgator scan && npx navgator impact <front-door>` after cutover | No orphan callers, route count dropped by ≥ 6 |
852
+ | **NEW (v2.2) — RAGAS Faithfulness** | ≥ 0.85 on 200-query fixture; no synthesis claim without retrieved-context support |
853
+ | **NEW (v2.2) — RAGAS Context Recall** | ≥ 0.80; RRF + multi-query bring in the right docs (vs ~62% v2.1 vector-only baseline) |
854
+ | **NEW (v2.2) — RAGAS Context Precision** | ≥ 0.75; reranker + CRAG gate filter noise |
855
+ | **NEW (v2.2) — RAGAS Answer Relevance** | ≥ 0.85; synthesis answers the asked question |
856
+ | **NEW (v2.2) — Tier 3 schema-validation rate** | ≥ 99.5% on Groq `openai/gpt-oss-120b` strict mode; trigger Phase 11 swap if < 97% |
857
+ | **NEW (v2.2) — CRAG gate trigger rate** | 5–15% of queries (too low = gate not firing; too high = retrieval is broken) |
858
+ | **NEW (v2.2) — Provider abstraction** — swap reranker model in Phase 11 with one config change | Provider swap works config-only, no code edit elsewhere |
859
+
860
+ ---
861
+
862
+ ## 7. Risks & Mitigations
863
+
864
+ > **Intent of this section:** Surface the things most likely to go wrong, with their pre-decided mitigations, so review cycles don't have to rediscover them. If you find yourself proposing a workaround during implementation, check this table first — it may already be addressed.
865
+
866
+ | Risk | Likelihood | Impact | Mitigation |
867
+ |---|---|---|---|
868
+ | Cascade misclassifies common queries | Medium | High UX | Phase 1 shadow mode + 24-48h soak before any flip |
869
+ | Synthesis prompt regression on `format=default` | Medium | UX | Keep current pyramid prompt verbatim; only add new format-aware variant |
870
+ | HyDE prompt overfitting / off-topic expansions | Low | Mid | 1.5s timeout already in original; keep length-validation guard |
871
+ | Trending v1↔v2 cutover breaks `/markettrends` page | Medium | High | Backport features one mode at a time; smoke test each |
872
+ | Frontend consolidation breaks one of 5 search pages | Medium | Mid | Page-specific wrappers preserve URLs; visual regression via IBR |
873
+ | Deleting `intelligence/route.ts` breaks an unknown consumer | Low | Low | Strict grep + `navgator impact` before delete |
874
+ | Redis cost bump from intent cache | Very low | Negligible | Same Upstash instance, 24h TTL on a small surface |
875
+ | Provider outage on Tier 3 (Groq) | Low | Mid | Existing circuit-breaker pattern + Haiku 4.5 fallback already designed |
876
+ | Eval harness drift after Phase 8 prompt changes | Medium | Mid | Re-run eval after every prompt-builder save; fail merge if accuracy drops |
877
+
878
+ ---
879
+
880
+ ## 8. What This Plan Deliberately Does NOT Do
881
+
882
+ > **Intent of this section:** Hold the line on scope. Each bullet names something a reasonable person might think this plan should also do, and explains why it stays out. If a future change request lands in this list, it earns its own plan rather than getting bolted onto this one.
883
+
884
+ - ❌ Replace synthesis provider (Groq → other). Orthogonal.
885
+ - ❌ Move to a learned router (RouteLLM-style). Engineering cost dwarfs marginal gain at current scale ([TianPan](https://tianpan.co/blog/2025-11-03-llm-routing-model-cascades)). Revisit at 100k+ qpd.
886
+ - ❌ Unify `domain = general | research` into one column. Cosmetic; planner-level fan-out gets the same UX without a migration.
887
+ - ❌ Big-bang rewrite. Every phase ships behind a flag.
888
+ - ❌ New RAG pattern. You already have all three (Pipeline / Agentic / KG).
889
+ - ❌ Schema migration. Postgres stays as-is.
890
+ - ❌ Railway worker topology change. The 4-service map stays as documented in `docs/04-operations/RAILWAY_WORKERS.md`.
891
+ - ❌ Vercel cron change. Same 11 schedules.
892
+
893
+ ---
894
+
895
+ ## 8.5 Data Flow Sequences — BEFORE and AFTER
896
+
897
+ > **Intent of this section:** Walk a single real query through every box in §1 and §2 step by step, showing what data exists at each stage and where it gets lost (BEFORE) or honored (AFTER). This is the section to read if you want to *understand* the bug rather than just see the architecture diagrams. The worked example is the actual failing query the user hit; the integration-points table at the end shows side-by-side change at every system boundary.
898
+
899
+ These diagrams trace a single query end-to-end. Worked example throughout: **"What's the latest AI trends for research and product releases. share this in a table"** — the actual query that triggered this plan.
900
+
901
+ Conventions:
902
+ - Steps numbered 1, 2, 3 …
903
+ - Each step shows: **what fires**, **what data is shaped**, **integration point**.
904
+ - 🔴 marks where the format intent gets lost (BEFORE) or honored (AFTER).
905
+ - 💾 marks integration with persistent stores.
906
+ - ⚡ marks cache lookups.
907
+
908
+ ---
909
+
910
+ ### 8.5.1 BEFORE — Current data flow (where it breaks)
911
+
912
+ ```
913
+ ┌──────────────────────────────────────────────────────────────────────────────┐
914
+ │ USER │
915
+ │ Types: "What's the latest AI trends for research and product releases. │
916
+ │ share this in a table" │
917
+ └──────────────────────────────────────────────────────────────────────────────┘
918
+ │ keystrokes
919
+
920
+ ┌──────────────────────────────────────────────────────────────────────────────┐
921
+ │ STEP 1 · FRONTEND · components/IntelligentSearchBar.tsx │
922
+ │ │
923
+ │ Debounce(300ms) → onSubmit │
924
+ │ Local state: { query: "...", timeHorizonDays: 7, mode: "all" } │
925
+ │ 📤 fires TWO parallel fetches: │
926
+ │ fetch('/api/intelligent-search', POST, body=above) │
927
+ │ fetch('/api/intelligent-search/summary', POST, body=above) │
928
+ └──────────────────────────────────────────────────────────────────────────────┘
929
+ │ HTTPS / Vercel Edge
930
+
931
+ ┌──────────────────────────────────────────────────────────────────────────────┐
932
+ │ STEP 2 · VERCEL · app/api/intelligent-search/route.ts (1582 lines) │
933
+ │ │
934
+ │ ⚡ in-memory cache check (per-instance, lost on cold start) │
935
+ │ Body parsed → { query, mode, timeHorizonDays } │
936
+ │ │
937
+ │ Inline regex intent detection (route.ts:1170-1181): │
938
+ │ wantsTimeline = /timeline|release history|releases over.../i │
939
+ │ wantsRelease = /release|launch|new model|shipped/i ✓ matches │
940
+ │ wantsComparison = /compare|versus|benchmark/i │
941
+ │ 🔴 wantsTable / wantsChart / wantsTornado: DOES NOT EXIST │
942
+ │ │
943
+ │ Data shape: { query, mode, horizon, wantsRelease: true } │
944
+ └──────────────────────────────────────────────────────────────────────────────┘
945
+ │ delegates to
946
+
947
+ ┌──────────────────────────────────────────────────────────────────────────────┐
948
+ │ STEP 3 · LIB · lib/search/query-router.ts │
949
+ │ │
950
+ │ performBasicAnalysis(query) │
951
+ │ → temporal words, entity extraction, complexity heuristics │
952
+ │ → output: QueryAnalysis { type, complexity, entities, searchStrategy } │
953
+ │ │
954
+ │ shouldUseEnhancedAnalysis(query) │
955
+ │ if true → performEnhancedAnalysis() → OpenAI call │
956
+ │ Classifies as: factual | conceptual | temporal | entity | mixed │
957
+ │ Strategy: vector-primary | keyword-primary | hybrid-weighted | … │
958
+ │ │
959
+ │ ⚡ in-memory cache (5min TTL, max 500 entries) │
960
+ │ ⚠ format/domain NOT in QueryAnalysis schema │
961
+ └──────────────────────────────────────────────────────────────────────────────┘
962
+ │ feeds analysis to
963
+
964
+ ┌──────────────────────────────────────────────────────────────────────────────┐
965
+ │ STEP 4 · LIB · lib/knowledge-graph/intelligent-query-engine.ts │
966
+ │ │
967
+ │ IntelligentQueryEngine.query(query, queryAnalysis) │
968
+ │ │ │
969
+ │ ├─ 4a. Vector search (pgvector) │
970
+ │ │ 💾 SELECT FROM article_embeddings ORDER BY <-> embedding LIMIT 50 │
971
+ │ │ │
972
+ │ ├─ 4b. Keyword search │
973
+ │ │ 💾 SELECT FROM articles WHERE title ILIKE … LIMIT 30 │
974
+ │ │ │
975
+ │ ├─ 4c. Release branch (because wantsRelease=true, route.ts:903-923) │
976
+ │ │ 💾 SELECT FROM release WHERE releasedAt >= NOW()-INTERVAL '2 years'│
977
+ │ │ │
978
+ │ ├─ 4d. Summary join │
979
+ │ │ 💾 SELECT summaryJson FROM summary WHERE articleId IN (…) │
980
+ │ │ │
981
+ │ └─ 4e. Groq reranker │
982
+ │ Sends top 50 to Groq llama-3.1-70b for relevance scoring │
983
+ │ │
984
+ │ 🔴 NEVER queries: papers, OpenAlex, Semantic Scholar, arXiv │
985
+ │ 🔴 NEVER fans out by domain — "research" interpreted as articles-mentioning-│
986
+ │ research, not papers │
987
+ │ │
988
+ │ Data shape: { results: Article[], releases: Release[], rerankedScores } │
989
+ └──────────────────────────────────────────────────────────────────────────────┘
990
+ │ raw results returned to route
991
+
992
+ ┌──────────────────────────────────────────────────────────────────────────────┐
993
+ │ STEP 5 · VERCEL · /api/intelligent-search/route.ts (continues) │
994
+ │ │
995
+ │ Builds response object: │
996
+ │ { │
997
+ │ success: true, │
998
+ │ results: Article[], │
999
+ │ structuredResults: [ │
1000
+ │ { kind: 'timeline', events: [...releases...] } ← if wantsTimeline │
1001
+ │ ], │
1002
+ │ timelineEvents: [...], │
1003
+ │ pyramidSummary: null ← filled by parallel summary call │
1004
+ │ } │
1005
+ │ │
1006
+ │ 🔴 Allowed structuredResults kinds: 'timeline', 'benchmark' — NO 'table' │
1007
+ │ 🔴 Format directive "share this in a table" already gone forever │
1008
+ └──────────────────────────────────────────────────────────────────────────────┘
1009
+ │ ──────────► JSON response (one half)
1010
+
1011
+ PARALLEL ──────►│
1012
+
1013
+ ┌──────────────────────────────────────────────────────────────────────────────┐
1014
+ │ STEP 6 · VERCEL · /api/intelligent-search/summary/route.ts (1082 lines) │
1015
+ │ │
1016
+ │ Receives same query independently │
1017
+ │ selectSynthesisModel() picks Groq llama-3.1-70b primary │
1018
+ │ │
1019
+ │ Calls articles search again (often redundantly — separate cache key) │
1020
+ │ │
1021
+ │ System prompt: │
1022
+ │ "OUTPUT FORMAT: valid markdown only. │
1023
+ │ Task: Summarize N articles about <query> for Smart-Brevity reader. │
1024
+ │ Structure: # Key Insight / ## Supporting Points / ## Important Nuances │
1025
+ │ / ## Key Entities" │
1026
+ │ │
1027
+ │ 🔴 Prompt is contractually markdown-only — even if the route received a │
1028
+ │ format hint, the LLM cannot emit a table │
1029
+ │ │
1030
+ │ Returns: { type: 'markdown', content: '# Key Insight\n…' } │
1031
+ │ │
1032
+ │ Circuit breakers on Groq + OpenAI handle failover │
1033
+ └──────────────────────────────────────────────────────────────────────────────┘
1034
+ │ ──────────► JSON response (other half)
1035
+
1036
+ ┌──────────────────────────────────────────────────────────────────────────────┐
1037
+ │ STEP 7 · FRONTEND · IntelligentSearchBar.tsx + PyramidSummary.tsx │
1038
+ │ │
1039
+ │ Awaits both fetches │
1040
+ │ parseMarkdownPyramid(pyramidSummary.content) → AST │
1041
+ │ │
1042
+ │ Renders in order: │
1043
+ │ 1. PyramidSummary (markdown prose) │
1044
+ │ 2. EventTimeline (release events, NOT a table) │
1045
+ │ 3. FactsChart (if any extracted) │
1046
+ │ 4. Article cards (top results) │
1047
+ │ │
1048
+ │ 🔴 No table component is ever instantiated │
1049
+ │ 🔴 The user's "share this in a table" instruction has been silently lost │
1050
+ │ at three layers: route regex, query analysis, synthesis prompt │
1051
+ └──────────────────────────────────────────────────────────────────────────────┘
1052
+
1053
+
1054
+ USER SEES:
1055
+ ┌──────────────────────────────────────┐
1056
+ │ # Key Insight │
1057
+ │ AI is becoming increasingly… │
1058
+ │ │
1059
+ │ ## Supporting Points │
1060
+ │ • Conversational Interfaces — … │
1061
+ │ • Spatial Services — … │
1062
+ │ • Simulation-Based Manufacturing — …│
1063
+ │ │
1064
+ │ NO TABLE. │
1065
+ └──────────────────────────────────────┘
1066
+ ```
1067
+
1068
+ **Where data is lost:**
1069
+
1070
+ | Step | Loss |
1071
+ |---|---|
1072
+ | 2 | Format directive ("table") — regex doesn't look for it |
1073
+ | 3 | Format/domain — `QueryAnalysis` schema has no fields for them |
1074
+ | 4 | Domain fan-out — "research" routed to articles, not papers |
1075
+ | 6 | Format expressivity — synthesis prompt is markdown-only |
1076
+ | 7 | No table renderer exists in the result-shell anyway |
1077
+
1078
+ ---
1079
+
1080
+ ### 8.5.2 AFTER — Target data flow (where it works)
1081
+
1082
+ Same query as input. Annotations show transformations and integration points.
1083
+
1084
+ ```
1085
+ ┌──────────────────────────────────────────────────────────────────────────────┐
1086
+ │ USER │
1087
+ │ Types: "What's the latest AI trends for research and product releases. │
1088
+ │ share this in a table" │
1089
+ └──────────────────────────────────────────────────────────────────────────────┘
1090
+ │ keystrokes
1091
+
1092
+ ┌──────────────────────────────────────────────────────────────────────────────┐
1093
+ │ STEP 1 · FRONTEND · components/search/SearchBar.tsx (canonical) │
1094
+ │ │
1095
+ │ Debounce(300ms) → onSubmit │
1096
+ │ Hook chooses transport based on prop: │
1097
+ │ streamMode=false → useSearch() → POST /api/search │
1098
+ │ streamMode=true → useSearchStream() → GET /api/search?stream=true │
1099
+ │ → EventSource subscription │
1100
+ │ │
1101
+ │ Body shape: │
1102
+ │ { query: "...", sessionId, hints?: { domain?, horizonDays?, format? } } │
1103
+ └──────────────────────────────────────────────────────────────────────────────┘
1104
+ │ HTTPS / Vercel Edge
1105
+
1106
+ ┌──────────────────────────────────────────────────────────────────────────────┐
1107
+ │ STEP 2 · VERCEL · app/api/search/route.ts (orchestrator, ~150 lines) │
1108
+ │ │
1109
+ │ Parses body → { query, hints, sessionId } │
1110
+ │ │
1111
+ │ Pipeline: │
1112
+ │ intent = await classifyIntent(query, hints) ── Step 3 │
1113
+ │ plan = planRetrieval(intent) ── Step 4 │
1114
+ │ retrieval = await fanOutRetrieve(plan) ── Step 5 │
1115
+ │ structured = normalize(intent, retrieval) ── Step 6 │
1116
+ │ prose = await synthesize(intent, structured, plan) ── Step 7 │
1117
+ │ return respond({ intent, prose, structured, plan, diag }) ── Step 8 │
1118
+ └──────────────────────────────────────────────────────────────────────────────┘
1119
+ │ classifyIntent()
1120
+
1121
+ ┌──────────────────────────────────────────────────────────────────────────────┐
1122
+ │ STEP 3 · LIB · lib/search/intent/ (3-tier cascade) │
1123
+ │ │
1124
+ │ ⚡ Step 3.0 · intent-cache.ts │
1125
+ │ Check Redis: GET search:intent:<hash(query)> (24h TTL) │
1126
+ │ HIT → return cached Intent (microseconds, no work) │
1127
+ │ MISS → continue to T1 │
1128
+ │ │
1129
+ │ ┌──────────────────────────────────────────────────────────────┐ │
1130
+ │ │ Step 3.1 · tier1-regex.ts target: 5–20 ms │ │
1131
+ │ │ │ │
1132
+ │ │ Patterns checked (selected): │ │
1133
+ │ │ FORMAT: /\btable|grid|tabular|spreadsheet\b/i ✓ MATCH │ │
1134
+ │ │ /\bchart|diagram|graph|tornado\b/i │ │
1135
+ │ │ /\btimeline|history\b/i │ │
1136
+ │ │ DOMAIN: /\bresearch|paper|arxiv|study\b/i ✓ MATCH │ │
1137
+ │ │ /\brelease|launch|product\b/i ✓ MATCH │ │
1138
+ │ │ COMPARE: /\bvs\.?|versus|compare|benchmark\b/i │ │
1139
+ │ │ HORIZON: /\blast\s+\d+\s+(day|week|month|year)/i │ │
1140
+ │ │ │ │
1141
+ │ │ Builds Intent partial: │ │
1142
+ │ │ { format: 'table', │ │
1143
+ │ │ domains: ['research','releases'], │ │
1144
+ │ │ horizonDays: 7 /* default */, │ │
1145
+ │ │ confidence: 0.92, tier: 1 } │ │
1146
+ │ │ │ │
1147
+ │ │ confidence ≥ 0.85? ✓ YES → SHORT-CIRCUIT, return Intent │ │
1148
+ │ │ confidence < 0.85? → fall through to T2 │ │
1149
+ │ └──────────────────────────────────────────────────────────────┘ │
1150
+ │ │
1151
+ │ ┌──────────────────────────────────────────────────────────────┐ │
1152
+ │ │ Step 3.2 · tier2-embed.ts target: 20–50 ms │ │
1153
+ │ │ Compute query embedding (existing OpenAI text-embed-3) │ │
1154
+ │ │ ⚡ ~30 prototype embeddings cached in Redis │ │
1155
+ │ │ Cosine match top-1 ≥ 0.75 → map to canonical Intent │ │
1156
+ │ │ confidence ≥ 0.80? → return; else fall through │ │
1157
+ │ └──────────────────────────────────────────────────────────────┘ │
1158
+ │ │
1159
+ │ ┌──────────────────────────────────────────────────────────────┐ │
1160
+ │ │ Step 3.3 · tier3-llm.ts target: 150–600 ms │ │
1161
+ │ │ Groq Llama-3.1-8B (primary) / Haiku 4.5 (fallback) │ │
1162
+ │ │ response_format: { type:'json_schema', schema: IntentZod } │ │
1163
+ │ │ temperature: 0 │ │
1164
+ │ │ Always returns; low-confidence outputs still used + logged │ │
1165
+ │ └──────────────────────────────────────────────────────────────┘ │
1166
+ │ │
1167
+ │ 💾 SET search:intent:<hash> = Intent (24h TTL) │
1168
+ │ │
1169
+ │ Final Intent (this query): │
1170
+ │ { domains: ['research', 'releases', 'articles'], │
1171
+ │ retrievalPattern: 'agentic', /* multi-domain → agentic */ │
1172
+ │ format: 'table', │
1173
+ │ chartSubtype: undefined, │
1174
+ │ horizonDays: 7, │
1175
+ │ comparisonTargets: [], │
1176
+ │ topicKeywords: ['AI', 'trends', 'research', 'releases'], │
1177
+ │ confidence: 0.92, │
1178
+ │ tier: 1 } │
1179
+ └──────────────────────────────────────────────────────────────────────────────┘
1180
+ │ planRetrieval(intent)
1181
+
1182
+ ┌──────────────────────────────────────────────────────────────────────────────┐
1183
+ │ STEP 4 · LIB · lib/search/planner.ts (pure function, no LLM, no I/O) │
1184
+ │ │
1185
+ │ Rules (deterministic, debuggable): │
1186
+ │ domains has 'research' AND domains.length > 1 → 'agentic' ✓ matches │
1187
+ │ domains has 'kg' OR comparisonTargets → 'kg' │
1188
+ │ domains.length === 1 → 'pipeline' │
1189
+ │ else → 'hybrid' │
1190
+ │ │
1191
+ │ Output RetrievalPlan: │
1192
+ │ { │
1193
+ │ pattern: 'agentic', │
1194
+ │ sources: [ │
1195
+ │ { kind: 'articles', branch: 'pipeline', horizonDays: 7 }, │
1196
+ │ { kind: 'releases', branch: 'pipeline', horizonDays: 365 }, │
1197
+ │ { kind: 'papers', branch: 'kg', horizonDays: 30 }, │
1198
+ │ ], │
1199
+ │ merge: 'union-by-recency', │
1200
+ │ expandQuery: false, /* not vague — 13 tokens */ │
1201
+ │ synthesize: true, │
1202
+ │ } │
1203
+ └──────────────────────────────────────────────────────────────────────────────┘
1204
+ │ fanOutRetrieve(plan)
1205
+
1206
+ ┌──────────────────────────────────────────────────────────────────────────────┐
1207
+ │ STEP 5 · LIB · lib/search/retrieval/ (parallel fan-out) │
1208
+ │ │
1209
+ │ Promise.all([ │
1210
+ │ ┌─────────────────────────────────────────┐ │
1211
+ │ │ 5a. pipeline-rag.ts (articles) │ │
1212
+ │ │ IF intent.tier===3 OR query.length<25 │ │
1213
+ │ │ ⤳ HyDE expansion (query-expansion) │ │
1214
+ │ │ Groq llama-3.3-70b rewrites vague │ │
1215
+ │ │ query as 2-3 sentence excerpt │ │
1216
+ │ │ (recovered from search/semantic) │ │
1217
+ │ │ 💾 article_embeddings (pgvector) │ │
1218
+ │ │ 💾 articles (keyword fallback if <3) │ │
1219
+ │ │ 💾 summary (join) │ │
1220
+ │ │ ⤳ Groq reranker (top 50) │ │
1221
+ │ └─────────────────────────────────────────┘ │
1222
+ │ │
1223
+ │ ┌─────────────────────────────────────────┐ │
1224
+ │ │ 5b. pipeline-rag.ts (releases) │ │
1225
+ │ │ 💾 release WHERE releasedAt >= cutoff │ │
1226
+ │ │ 💾 join primaryArticle │ │
1227
+ │ └─────────────────────────────────────────┘ │
1228
+ │ │
1229
+ │ ┌─────────────────────────────────────────┐ │
1230
+ │ │ 5c. kg-rag.ts (research papers) │ │
1231
+ │ │ 💾 entities WHERE │ │
1232
+ │ │ type='paper' AND domain='research'│ │
1233
+ │ │ AND publishedAt >= cutoff │ │
1234
+ │ │ ORDER BY citationCount delta DESC │ │
1235
+ │ │ 💾 entity_pairs (citation chain) │ │
1236
+ │ │ ⤳ optional: arXiv inline fetch for │ │
1237
+ │ │ fresh metadata │ │
1238
+ │ └─────────────────────────────────────────┘ │
1239
+ │ ]) │
1240
+ │ │
1241
+ │ Merge (union-by-recency, dedup by canonical id) │
1242
+ │ Output: { articles[], releases[], papers[], evidence[] } │
1243
+ └──────────────────────────────────────────────────────────────────────────────┘
1244
+ │ normalize(intent, retrieval)
1245
+
1246
+ ┌──────────────────────────────────────────────────────────────────────────────┐
1247
+ │ STEP 6 · LIB · lib/search/normalize.ts │
1248
+ │ │
1249
+ │ Reads intent.format and intent.chartSubtype, builds StructuredResult[]: │
1250
+ │ │
1251
+ │ format === 'table' → emit two table panels: │
1252
+ │ { │
1253
+ │ kind: 'table', │
1254
+ │ title: 'Recent Product Releases', │
1255
+ │ columns: ['Date','Vendor','Product','Version','Source'], │
1256
+ │ rows: [ │
1257
+ │ ['2026-04-25','Anthropic','Claude','Opus 4.7','anthropic.com'], │
1258
+ │ ['2026-04-22','OpenAI', 'GPT-…', 'v…', 'openai.com'], │
1259
+ │ ... │
1260
+ │ ], │
1261
+ │ source: { releases: 'postgres.release' } │
1262
+ │ }, │
1263
+ │ { │
1264
+ │ kind: 'table', │
1265
+ │ title: 'Recent Research Papers', │
1266
+ │ columns: ['Date','Paper','Authors','Venue','Citations','Link'], │
1267
+ │ rows: [...], │
1268
+ │ source: { papers: 'postgres.entities[domain=research]' } │
1269
+ │ } │
1270
+ │ │
1271
+ │ Always also emit (when format !== 'graph'): │
1272
+ │ { kind: 'timeline', events: […releases by date…] } ← contextual │
1273
+ │ │
1274
+ │ IF user had requested 'tornado diagram': │
1275
+ │ { kind: 'unsupported-format', │
1276
+ │ requested: 'tornado', │
1277
+ │ fallbackKind: 'chart', │
1278
+ │ reason: 'Tornado diagrams need signed impact values; news data does │
1279
+ │ not provide that. Showing ranked-bar of theme frequency.' } │
1280
+ │ │
1281
+ │ 🟢 Format intent has been HONORED at the data layer │
1282
+ └──────────────────────────────────────────────────────────────────────────────┘
1283
+ │ synthesize(intent, structured, plan)
1284
+
1285
+ ┌──────────────────────────────────────────────────────────────────────────────┐
1286
+ │ STEP 7 · LIB · lib/search/synthesizer.ts │
1287
+ │ │
1288
+ │ Selects prompt by format: │
1289
+ │ format === 'default' → lib/search/prompts/synthesis-default.md │
1290
+ │ format !== 'default' → lib/search/prompts/synthesis-with-tables.md │
1291
+ │ │
1292
+ │ synthesis-with-tables.md (excerpt): │
1293
+ │ "Write ONE paragraph (≤80 words) summarizing the trend across the rows │
1294
+ │ in `<tables>`. Do NOT invent rows. Do NOT format as a list. Mention │
1295
+ │ the dominant theme and one notable outlier. Plain prose." │
1296
+ │ │
1297
+ │ LLM (Groq llama-3.1-70b primary) returns: │
1298
+ │ "Recent AI activity skews toward research with 12 new papers on …, │
1299
+ │ while the release calendar is dominated by Anthropic and OpenAI. │
1300
+ │ Notable outlier: …" │
1301
+ │ │
1302
+ │ 🟢 LLM narrates, never fabricates table rows │
1303
+ └──────────────────────────────────────────────────────────────────────────────┘
1304
+ │ respond()
1305
+
1306
+ ┌──────────────────────────────────────────────────────────────────────────────┐
1307
+ │ STEP 8 · VERCEL · /api/search/route.ts (returns) │
1308
+ │ │
1309
+ │ JSON body: │
1310
+ │ { │
1311
+ │ intent: Intent, /* echoed for UI transparency */ │
1312
+ │ prose: { kind:'pyramid', markdown: '…' }, │
1313
+ │ structuredResults: [ │
1314
+ │ { kind:'table', title:'Recent Product Releases', columns, rows }, │
1315
+ │ { kind:'table', title:'Recent Research Papers', columns, rows }, │
1316
+ │ { kind:'timeline', events: [...] } │
1317
+ │ ], │
1318
+ │ diagnostics: { tier: 1, latencyMs: 187, cacheHit: false, │
1319
+ │ model: 'groq:llama-3.1-70b' } │
1320
+ │ } │
1321
+ │ │
1322
+ │ IF stream=true: same content emitted as SSE chunks: │
1323
+ │ event: intent data: {…} ← <50ms │
1324
+ │ event: keyword data: {…} ← <200ms (early article hits) │
1325
+ │ event: table data: {…} ← <500ms (releases query done) │
1326
+ │ event: table data: {…} ← <800ms (papers query done) │
1327
+ │ event: timeline data: {…} │
1328
+ │ event: prose data: {…} ← last (synthesis is slowest) │
1329
+ │ event: complete data: { totalLatencyMs } │
1330
+ └──────────────────────────────────────────────────────────────────────────────┘
1331
+ │ JSON or SSE
1332
+
1333
+ ┌──────────────────────────────────────────────────────────────────────────────┐
1334
+ │ STEP 9 · FRONTEND · components/search/SearchResults.tsx │
1335
+ │ │
1336
+ │ for (const r of structuredResults) { │
1337
+ │ switch (r.kind) { │
1338
+ │ case 'table': <ResultTable {...r} /> ← 🟢 RENDERS │
1339
+ │ case 'chart': <ResultChart {...r} /> │
1340
+ │ case 'timeline': <EventTimeline {...r} /> │
1341
+ │ case 'graph': <ResultGraph {...r} /> │
1342
+ │ case 'unsupported-format': <UnsupportedFormat {...r} /> │
1343
+ │ } │
1344
+ │ } │
1345
+ │ <PyramidSummary content={prose.markdown} /> ← AT TOP, 1 paragraph │
1346
+ │ │
1347
+ │ Streaming variant: │
1348
+ │ hooks/useSearchStream.ts subscribes via EventSource; │
1349
+ │ sets each event into a discriminated state slot; │
1350
+ │ SearchResults re-renders incrementally as each chunk arrives │
1351
+ └──────────────────────────────────────────────────────────────────────────────┘
1352
+
1353
+
1354
+ USER SEES:
1355
+ ┌─────────────────────────────────────────────────────────────┐
1356
+ │ Recent AI activity skews toward research with 12 new │
1357
+ │ papers, while releases are dominated by Anthropic and │
1358
+ │ OpenAI. Notable outlier: … │
1359
+ │ │
1360
+ │ ┌───────────────────────────────────────────────────────┐ │
1361
+ │ │ Recent Product Releases │ │
1362
+ │ │ ┌──────────┬────────────┬─────────┬────────┬───────┐ │ │
1363
+ │ │ │ Date │ Vendor │ Product │ Version│ Source│ │ │
1364
+ │ │ │ 2026-04…│ Anthropic │ Claude │ Opus… │ … │ │ │
1365
+ │ │ │ 2026-04…│ OpenAI │ GPT-… │ v… │ … │ │ │
1366
+ │ │ └──────────┴────────────┴─────────┴────────┴───────┘ │ │
1367
+ │ └───────────────────────────────────────────────────────┘ │
1368
+ │ │
1369
+ │ ┌───────────────────────────────────────────────────────┐ │
1370
+ │ │ Recent Research Papers │ │
1371
+ │ │ ┌──────────┬─────────┬───────────┬──────┬───────┬───┐ │ │
1372
+ │ │ │ Date │ Paper │ Authors │ Venue│ Citns │ … │ │ │
1373
+ │ │ └──────────┴─────────┴───────────┴──────┴───────┴───┘ │ │
1374
+ │ └───────────────────────────────────────────────────────┘ │
1375
+ │ │
1376
+ │ Timeline · April 2026 │
1377
+ │ ●━━━●━━━●━━━● releases plotted by date │
1378
+ └─────────────────────────────────────────────────────────────┘
1379
+ ```
1380
+
1381
+ ---
1382
+
1383
+ ### 8.5.3 Side-by-side at each integration point
1384
+
1385
+ | Integration point | BEFORE | AFTER |
1386
+ |---|---|---|
1387
+ | **User → Frontend** | 8+ search components, inconsistent | One `SearchBar`, optional streaming |
1388
+ | **Frontend → Vercel** | TWO parallel POSTs to overlapping routes | ONE POST, optional `?stream=true` |
1389
+ | **Intent detection** | Inline regex in route + partial QueryRouter, schema lacks format/domain | 3-tier cascade with typed `Intent`, format & domain first-class |
1390
+ | **Cache (intent)** | None | Redis 24h LRU keyed on query hash |
1391
+ | **Cache (results)** | In-memory per-Vercel-instance, lost on cold start | Redis (existing `TrendingCache`/`temporal-cache` reused for retrieval) |
1392
+ | **Postgres reads** | Articles + releases only | Articles + releases + papers + entity graph (multi-domain fan-out) |
1393
+ | **pgvector** | One vector pass | Same, plus optional HyDE expansion for vague queries |
1394
+ | **KG / arXiv** | Isolated under `/api/research/*`, not reached from search | Reached as KG-RAG branch in retrieval |
1395
+ | **Synthesis prompt** | Markdown-only, can't honor format | Two prompts (default / with-tables), prompt-builder governance |
1396
+ | **Response shape** | `results[]` + optional `structuredResults` (`timeline`, `benchmark` only) | `intent` + `prose` + `structuredResults[]` (5 kinds incl. `table`, `chart`, `unsupported-format`) |
1397
+ | **Vercel → Frontend** | Two awaited fetches, then render | One JSON response, OR SSE stream with progressive paint |
1398
+ | **Frontend render** | Pyramid + maybe timeline, no table | Format-aware `SearchResults` shell renders correct kind |
1399
+ | **Format directive** | 🔴 Lost at three layers | 🟢 Honored at data layer; honestly downgraded if unsupported |
1400
+ | **Railway workers** | ✅ Untouched | ✅ Untouched (4 services keep current queues) |
1401
+ | **Vercel cron** | ✅ Untouched | ✅ Untouched (11 schedules keep) |
1402
+ | **Postgres schema** | ✅ Untouched | ✅ Untouched (optional `intent_log` later) |
1403
+ | **Provider failover** | Existing Groq/OpenAI circuit-breakers in summary route | Same pattern reused in T3 classifier and synthesizer |
1404
+
1405
+ ---
1406
+
1407
+ ### 8.5.4 Integration points worth calling out
1408
+
1409
+ 1. **Single front door via shared handler** — `/api/search` and `/api/intelligent-search` both export the same `runSearch` function. No 308 redirect, no URL semantics change. Backwards-compat is automatic; clients never see a difference.
1410
+ 2. **Intent → Plan → Retrieve are pure functions** of their inputs. No global state, no hidden coupling. Each is independently testable.
1411
+ 3. **Postgres is the source of truth for rows; LLM is the source of narration only.** The split eliminates row-level hallucination by construction.
1412
+ 4. **Redis becomes denser** but doesn't change shape — still one Upstash KV instance. New keyspaces are namespaced (`search:intent:*`, `search:embed:proto:*`).
1413
+ 5. **Railway and cron are off the critical path of this work entirely.** No deploy choreography needed; the workers keep ingesting and clustering as today.
1414
+ 6. **SSE is the only new transport.** Frontend gets `EventSource`; the backend reuses the same `respond()` function with a different writer. Same underlying pipeline.
1415
+ 7. **Prompt library is the new governance surface.** Every LLM call (T3 classifier, HyDE expansion, default synthesis, with-tables synthesis) reads from `lib/search/prompts/*.md`. `prompt-builder` scores and versions them. Hot-swap via env var.
1416
+
1417
+ ---
1418
+
1419
+ ## 9. Sources
1420
+
1421
+ > **Intent of this section:** Make the research traceable. Every architectural pattern recommended in this plan came from a real source — these are the ones cited inline. Internal sources (NavGator scan, file reads, memory references) are listed alongside external links. If you want to re-derive a recommendation from scratch, this is the bibliography.
1422
+
1423
+ **Web research (live, late-2025 / early-2026):**
1424
+ - [Meganova: The 3-Tier Routing Cascade — Rule-Based → Semantic → LLM](https://blog.meganova.ai/the-3-tier-routing-cascade-rule-based-semantic-llm/) — concrete tier latencies, 0.8 confidence threshold, 96% accuracy claim
1425
+ - [TianPan: LLM Routing and Model Cascades (Nov 2025)](https://tianpan.co/blog/2025-11-03-llm-routing-model-cascades) — when learned routing is worth it
1426
+ - [LogRocket: LLM Routing in Production](https://blog.logrocket.com/llm-routing-right-model-for-requests/) — strategy taxonomy
1427
+ - [NVIDIA AI Blueprints — llm-router](https://github.com/NVIDIA-AI-Blueprints/llm-router) — production reference impl
1428
+ - [Lanham: Pipeline vs Agentic vs KG RAG (Feb 2026)](https://medium.com/@Micheal-Lanham/pipeline-rag-vs-agentic-rag-vs-knowledge-graph-rag-what-actually-works-and-when-47a26649a457) — pattern selection by query shape
1429
+ - [Neo4j: GraphRAG and Agentic Architecture](https://neo4j.com/blog/developer/graphrag-and-agentic-architecture-with-neoconverse/) — KG + RAG hybrid memory
1430
+ - [ZBrain: Knowledge Graphs for Agentic AI](https://zbrain.ai/knowledge-graphs-for-agentic-ai/) — vector vs KG memory tradeoffs
1431
+ - [Data Nucleus: Agentic RAG Enterprise Guide 2026](https://datanucleus.dev/rag-and-agentic-ai/agentic-rag-enterprise-guide-2026) — adoption stats
1432
+ - [arXiv: Agentic RAG with Knowledge Graphs for Multi-Hop Reasoning](https://arxiv.org/abs/2507.16507) — academic basis
1433
+
1434
+ **v2.2 SOTA-validation sources (live web research 2026-04-28):**
1435
+ - Hybrid retrieval / RRF: [Supermemory hybrid search guide (Apr 2026)](https://blog.supermemory.ai/hybrid-search-guide/), [ParadeDB hybrid search manual](https://www.paradedb.com/blog/hybrid-search-in-postgresql-the-missing-manual), [Tiger Data Postgres hybrid search](https://www.tigerdata.com/blog/hybrid-search-postgres-you-probably-should), [Glaforge RRF (Feb 2026)](https://glaforge.dev/posts/2026/02/10/advanced-rag-understanding-reciprocal-rank-fusion-in-hybrid-search/), [Weaviate hybrid search](https://weaviate.io/blog/hybrid-search-explained), [Supabase hybrid search docs](https://supabase.com/docs/guides/ai/hybrid-search)
1436
+ - Query rewriting: [DMQR-RAG arXiv 2411.13154](https://arxiv.org/html/2411.13154v1), [DMFlow 6 advanced query transformation architectures](https://www.dmflow.chat/en/blog/rag-query-transformation-guide-6-advanced-architectures)
1437
+ - Reranker leaderboard: [agentset.ai reranker leaderboard](https://agentset.ai/rerankers), [Cohere Rerank 3.5 docs](https://docs.cohere.com/changelog/rerank-v3.5), [awesome-rerankers curated list](https://github.com/agentset-ai/awesome-rerankers), [Mixedbread BEIR benchmarks via Jina docs](https://jina.ai/news/maximizing-search-relevancy-and-rag-accuracy-with-jina-reranker/)
1438
+ - Knowledge graph RAG: [GraphRAG-Bench (ICLR'26)](https://github.com/GraphRAG-Bench/GraphRAG-Benchmark), [arXiv 2506.05690 — When to use Graphs in RAG](https://arxiv.org/html/2506.05690v3), [Graph Praxis 2026 practitioner guide](https://medium.com/graph-praxis/graph-rag-in-2026-a-practitioners-guide-to-what-actually-works-dca4962e7517)
1439
+ - Agentic RAG / CRAG: [arXiv 2501.09136 Agentic RAG Survey](https://arxiv.org/abs/2501.09136), [Data Nucleus enterprise guide 2026](https://datanucleus.dev/rag-and-agentic-ai/agentic-rag-enterprise-guide-2026)
1440
+ - Structured outputs / constrained decoding: [Groq Structured Outputs docs (verified 2026-04-28)](https://console.groq.com/docs/structured-outputs), [Groq supported models docs](https://console.groq.com/docs/models), [arXiv 2408.11061 StructuredRAG](https://arxiv.org/abs/2408.11061), [OpenAI Structured Outputs docs](https://developers.openai.com/api/docs/guides/structured-outputs)
1441
+ - Evaluation: [Ragas metrics docs](https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/), [PremAI RAG evaluation 2026](https://blog.premai.io/rag-evaluation-metrics-frameworks-testing-2026/)
1442
+ - Provider comparison (Groq vs OpenAI): [Groq pricing](https://groq.com/pricing), [Artificial Analysis Groq provider page](https://artificialanalysis.ai/providers/groq), [LLM API comparison 2026](https://www.morphllm.com/llm-api), [TokenMix Groq pricing breakdown](https://tokenmix.ai/blog/groq-api-pricing)
1443
+ - Research packet: `~/projects/example-app/.build-loop/research/2026-04-28-unified-search-sota-validation.md`
1444
+
1445
+ **Internal sources:**
1446
+ - Fresh NavGator scan 2026-04-28: 236 components, 2,580 connections, 1,889 files
1447
+ - File-level Explore pass over `/Users/you/example-app/`
1448
+ - Read all 6 candidate "orphan" route files end-to-end (5 confirmed orphan, 1 reclassified as live sibling per Codex review)
1449
+ - Memory: `reference_example_app_railway_workers.md`, `example-app.md`, `feedback_no_fake_stats.md`
1450
+ - `vercel.json` (11 cron entries, 60s function timeout)
1451
+ - `ecosystem.config.js` (PM2 layer for non-Railway deploys)
1452
+ - Existing tests: `tests/lib/search/{query-router,groq-reranker,rerank-policy}.test.ts`, `tests/unit/intelligent-query-engine-fallback.test.ts`
1453
+ - Existing eval harness: `scripts/evaluate-intelligent-search.ts`, `scripts/create-intelligent-search-datasets.ts`
1454
+
1455
+ ---
1456
+
1457
+ ## 11. Codex Review Log (2026-04-28)
1458
+
1459
+ > **Intent of this section:** Honest record of what the previous version got wrong. An external code-grounded review caught five concrete errors before any code shipped — this section names each one, the verification, and the correction applied. Most importantly, §11.2 captures the *root cause* of the errors so the same trap can be avoided in future plans (NavGator's connection detector misses template-literal `fetch()` calls). If you are about to delete a route, read §11.2 first.
1460
+
1461
+ External code-grounded review caught five errors in v2.0. All verified against the working tree, all corrected in v2.1.
1462
+
1463
+ ### 11.1 Errors caught and fixed
1464
+
1465
+ | # | Codex finding | Verification | Correction applied |
1466
+ |---|---|---|---|
1467
+ | 1 | `/api/trending-topics-v2` is **not** an orphan — 3 active callers | ✅ Confirmed: `lib/hooks/useTrendingTopicsCache.ts:256`, `components/TrendingTopics.tsx:121`, `components/TrendingTopicsEnhanced.tsx:422` | Reclassified as live sibling. Phase 5d reframed as "reconciliation" not "kill orphan". Removed from §6 delete list. |
1468
+ | 2 | `/api/kg/entities/search` has 3 KG-UI callers | ✅ Confirmed: `components/kg/KGSearchInterface.tsx:103`, `components/graph/EntitySearch.tsx:127`, `components/kg/EntityRelationshipPanel.tsx:412` | Removed from delete list. Plan now consolidates at lib layer (`lib/search/retrieval/entity-search.ts`), keeps both routes. |
1469
+ | 3 | `SearchModalNew` fetches `/api/intelligent-search`, not `/api/intelligence` | ✅ Confirmed: `components/SearchModalNew.tsx:51` `fetch('/api/intelligent-search', …)` | BEFORE diagram in §1.1 corrected. |
1470
+ | 4 | Recharts presence should be confirmed, not deferred | ✅ Confirmed: `package.json:133` `"recharts": "^3.8.0"` | §10 status changed from ❓ to ✅. |
1471
+ | 5 | 308 redirect for `/api/intelligent-search` is too risky | Method preservation, header propagation, observability all break in older clients | Replaced with **shared-handler shim** — both routes export the same `runSearch` function. Identical behavior, zero URL change, see §5.6. |
1472
+
1473
+ ### 11.2 Root cause of errors 1 and 2
1474
+
1475
+ Both came from over-trusting NavGator's `frontend-calls-api` connection detection, which only matches **literal-string** `fetch('…')` calls. Template literals (`fetch(\`/api/${endpoint}\`)`) and conditional URLs (`url = condition ? '/api/v2' : '/api/v1'`) are invisible to that detector.
1476
+
1477
+ **Mitigation now codified in §5.10:** every delete is gated by *both*:
1478
+ 1. `grep -rnE "['\"\`]/api/<name>['\"\`]?"` (literal)
1479
+ 2. `grep -rnE "/api/<name>"` (broader, catches template literals and string concatenation)
1480
+
1481
+ NavGator's `impact` is a useful directional signal but not authoritative for delete decisions.
1482
+
1483
+ ### 11.3 Scope discipline applied (Codex's milestone advice)
1484
+
1485
+ Codex recommended: *"greenlight the format-intent/structured-results milestone first; defer route deletion / trending consolidation / frontend consolidation until caller maps are corrected."*
1486
+
1487
+ Plan restructured into:
1488
+
1489
+ - **Milestone 1** (~5 days, merge-able alone): P0 eval, P1-P2 cascade in shadow, P3 structured results + UI, P4 planner extraction, P5 shared-handler orchestrator behind a flag, P8 partial prompt governance, P9 partial telemetry. **Old routes keep working untouched.**
1490
+ - **Milestone 2** (~4 days, defer until M1 has soaked): P5b-P5d orphan rescues + reconciliations, P6/P6b deletes, P7 T2 classifier, P8/P9 full.
1491
+
1492
+ This means the **format-intent fix can ship without ever deleting a route or consolidating frontend**. The two failing queries get solved in M1; cleanup is M2.
1493
+
1494
+ ### 11.4 Items still flagged for further verification
1495
+
1496
+ - Caller map for `/api/trending-topics` (live, base mode) and `/api/trending-topics-simple` — exact callers not enumerated; do this in Phase 5d before any reconciliation.
1497
+ - Caller map for the 8+ summarization routes — needed before any consolidation.
1498
+ - Caller map for `/api/entities/search` vs `/api/kg/entities/search` — confirm distinct consumers stay distinct, but share retrieval primitives.
1499
+
1500
+ ### 11.5 Updated risk profile
1501
+
1502
+ | Risk | Pre-Codex | Post-Codex |
1503
+ |---|---|---|
1504
+ | Delete a live route by mistake | Medium-high | Low (template-literal grep mandatory before any delete) |
1505
+ | Break clients via 308 redirect | Medium | Eliminated (shared handler) |
1506
+ | Break KG UI by deleting kg/entities/search | High | Eliminated (route preserved) |
1507
+ | Break trending UI by deleting v2 | High | Eliminated (treated as live sibling, reconciled before any delete) |
1508
+ | Misread eval/cascade infra status | Low (was already noted) | Low |
1509
+
1510
+ ---
1511
+
1512
+ ## 12. Status of claims
1513
+
1514
+ > **Intent of this section:** A confidence audit. Every load-bearing claim in the plan gets a status marker — ✅ verified by code · ✅ verified by NavGator · ⚠️ untested · TAG:INFERRED · ❓ uncertain — so a reader can see what was actually checked vs assumed. If you act on a TAG:INFERRED claim, verify it first.
1515
+
1516
+ - ✅ Verified by code: 60+ search routes, two parallel search surfaces, regex inline at `intelligent-search/route.ts:1170-1181`, `lib/search/query-router.ts` cascade-shaped, hard domain partition, markdown-only synthesis prompt, all 6 candidate "orphan" files read end-to-end (5 confirmed orphan + 1 misclassification corrected), eval harness present, 4-service Railway topology
1517
+ - ✅ Verified by NavGator (2026-04-28): 236 components, 2,580 connections, dead-route grep cross-check
1518
+ - ⚠️ Untested: latency/cost numbers in §3 — typical industry numbers, not measured against your stack
1519
+ - TAG:INFERRED: precise traffic distribution across the three tiers (~70/20/10 estimate from cited sources, not measured)
1520
+ - ✅ Recharts present at `package.json:133`, version `^3.8.0` — verified by Codex review
1521
+ - Some web sources are dated 2026 but published in early 2026 — directional, not statistically authoritative
1522
+ - ✅ **(v2.2) Verified at console.groq.com/docs/structured-outputs (2026-04-28):** strict `json_schema` mode (constrained decoding, 100% schema adherence) is supported on Groq for `openai/gpt-oss-20b` and `openai/gpt-oss-120b` only. Best-effort `json_schema` adds `openai/gpt-oss-safeguard-20b` and `meta-llama/llama-4-scout-17b-16e-instruct`. All other Groq models (Llama 3.3 70B, Llama 3.1 8B, Kimi K2, Qwen 3, etc.) only support `json_object` mode (valid JSON, no schema enforcement).
1523
+ - TAG:INFERRED **(v2.2)** RRF recall lift (~62-78% → ~84-91%) and reranker latency wins (500-2000ms → 50-200ms) are aggregate industry benchmarks, not measured on example-app fixtures. Phase 0 RAGAS eval must confirm magnitude before locking M2/M3.
1524
+ - ⚠️ Untested **(v2.2)** HippoRAG-vs-current-KG retrieval comparison — GraphRAG-Bench (ICLR'26) results are general; needs example-app-specific eval.
1525
+ - ✅ **(v2.2)** User provider preference — Groq cheaper+faster than OpenAI verified via live web search 2026-04-28: 3-12× tps, 5-10× cheaper, TTFT 120-250ms vs 300-700ms.
1526
+
1527
+ ---
1528
+
1529
+ ## 13. v2.2 SOTA Research Log (2026-04-28)
1530
+
1531
+ > **Intent of this section:** Honest record of what changed between v2.1 and v2.2 and why. v2.1 was correct in *shape*; v2.2 surfaces three implementation-layer upgrades (RRF, CRAG, multi-query) plus correct Groq-model selection per task. Mirrors §11 in style — names the finding, the verification, and the correction. If you are reviewing why a phase grew, this is the section that explains it.
1532
+
1533
+ ### 13.1 Trigger
1534
+
1535
+ User asked `/build-loop:research` to validate whether v2.1's approach reflects current SOTA for unified AI search across multi-source data, with explicit priority **accuracy first → speed → cost**, and a stated preference to stay on Groq for cost/latency reasons. User followed up clarifying that (a) Groq runs many LLMs, not one, so per-model capabilities matter; and (b) cross-provider model swaps (e.g. dedicated rerankers) should be a *last step after the architecture is working end-to-end*, not part of the initial cutover.
1536
+
1537
+ ### 13.2 Findings (live web research 2026-04-28)
1538
+
1539
+ | # | Finding | Source confidence | Action |
1540
+ |---|---|---|---|
1541
+ | 1 | The 3-tier cascade shape (regex → embed → LLM) is canonical 2026 production pattern. RouteLLM achieves ~95% of GPT-4 quality at ~15% cost. NVIDIA ships an llm-router blueprint. | T1/T2 — Meganova, NVIDIA, akshayghalme, TianPan | **Validated. No change.** v2.1 shape is SOTA. |
1542
+ | 2 | Pure pgvector retrieval ≈ 62-78% recall@10. Pure BM25/tsvector ≈ 65%. Vector + BM25 fused with **Reciprocal Rank Fusion (RRF, k=60)** ≈ 84-91% recall@10. RRF is now table-stakes for pgvector hybrid search. | T1 — Supermemory, ParadeDB, Tiger Data, Weaviate, Glaforge, Supabase docs, multiple benchmarks | **Added to Phase 4.** Pure code change, no new dependency. |
1543
+ | 3 | Modern "agentic RAG" expects a self-correction loop (Self-RAG, **CRAG**) that critiques retrieval quality post-rerank and reformulates or honest-downgrades when relevance is low. v2.1's "agentic" branch fans out and merges but never gates on quality. | T2 — arXiv 2501.09136, Data Nucleus 2026, multiple 2026 medium guides | **Added to Phase 4.** Reuses existing Groq Llama 3.3 70B as critic; honest-downgrade pattern already in plan for `unsupported-format`. |
1544
+ | 4 | DMQR-RAG (multi-query rewriting) shows +14.46% P@5 on FreshQA, +8% on HotpotQA multi-hop. **RAG-Fusion** (multi-query + RRF) is now the standard query-rewrite pattern. HyDE remains valuable specifically for *vague* queries; multi-query better for *complex/multi-hop*. | T1 — DMQR-RAG arXiv 2411.13154, multiple 2026 guides | **Phase 5b extended.** Multi-query is now primary, HyDE is the vague-query fallback, both routed by `intent.complexity`. |
1545
+ | 5 | GraphRAG-Bench (ICLR'26) shows **HippoRAG / HippoRAG2** lead multi-hop reasoning at 87-91% Evidence Recall, 85-88% Context Relevance. example-app's `entities` + `entity_pairs` schema fits HippoRAG's Personalized PageRank pattern *natively* — no new tables. LightRAG = lower latency but slightly lower accuracy. Microsoft GraphRAG = community-summary, expensive to index. | T1 — GraphRAG-Bench (ICLR'26), arXiv 2506.05690, Graph Praxis 2026 | **Added as Phase 5e (M2).** Uses existing schema; flag-gated; old KG retrieval is fallback. |
1546
+ | 6 | RAGAS reference-free metrics (faithfulness, context recall, context precision, answer relevance) are the 2026 standard for production RAG eval. v2.1 eval covered intent accuracy only. | T1 — Ragas docs, PremAI 2026 review, Maxim AI | **Phase 0 extended; merge gates added in §6.** Judged by existing Groq Llama 3.3 70B (~$5/full run). |
1547
+ | 7 | **Groq supports strict `json_schema`** (constrained decoding, 100% schema adherence) on `openai/gpt-oss-20b` and `openai/gpt-oss-120b` only. All other Groq models (including Llama 3.3 70B and 3.1 8B) only support `json_object` mode. The `gpt-oss-120b` model on Groq runs at ~500 tps with strict schema enforcement — better speed AND strictness than OpenAI Structured Outputs at lower cost. | T1 — verified live at console.groq.com/docs/structured-outputs (2026-04-28) | **Phase 1 Tier 3 model pinned to `openai/gpt-oss-120b` strict mode**, with `gpt-oss-20b` and `llama-4-scout` as fallbacks. **Stays on Groq.** Cross-provider OpenAI swap deferred to Phase 11 only if telemetry shows drift. |
1548
+ | 8 | **Reranker leadership has shifted in 2026** — Zerank-2 (1638 ELO leader), Voyage Rerank 2.5, Jina Reranker v3 (81.33% Hit@1 at 188ms) now lead Cohere on accuracy. Cohere Rerank 3.5/4.0 still competitive but no longer #1. Cross-encoders run 50-200ms vs LLM-rerank 500-2000ms with comparable or better accuracy. | T1/T2 — agentset.ai leaderboard, Mixedbread benchmarks, Jina docs, ZeroEntropy guide 2026 | **Deferred to Phase 11 (last step) per user direction.** v2.2 keeps Groq Llama 3.3 70B as reranker; Phase 4 wires the provider abstraction so swap is config-only later. |
1549
+ | 9 | Adaptive RAG: route by query complexity (simple → skip retrieval; medium → standard RAG; complex → multi-step). v2.1 routes by domain+format only. | T2 — Asai et al., 2026 surveys | **Added as Phase 10** plus an `intent.complexity` field threaded through Phase 5b's expansion routing. |
1550
+
1551
+ ### 13.3 What stayed unchanged in v2.1
1552
+
1553
+ - 3-tier cascade with 0.85/0.80 confidence gates ✅ canonical
1554
+ - Two-milestone shipping discipline ✅ correct cadence
1555
+ - Shared-handler shim over 308 redirect ✅ post-Codex correction holds
1556
+ - Format as first-class field with structured kinds ✅ aligned with StructuredRAG benchmark direction
1557
+ - Honest-downgrade pattern (`unsupported-format`) ✅ now extended to retrieval quality (`low-confidence`)
1558
+ - Pipeline / Agentic / KG split ✅ matches Lanham / Data Nucleus taxonomy
1559
+ - Provider abstraction in `lib/ai/*-service.ts` ✅ enables Phase 11 deferred swaps cleanly
1560
+
1561
+ ### 13.4 Provider strategy (v2.2)
1562
+
1563
+ Confirmed live 2026-04-28: Groq is **3-12× faster (tps)** and **5-10× cheaper** than comparable OpenAI/Anthropic calls. User preference is correct. Strategy is "stay on Groq, pick the right Groq model per task":
1564
+
1565
+ | Task | Groq model | Why |
1566
+ |---|---|---|
1567
+ | Tier 3 intent classifier | **`openai/gpt-oss-120b` strict** | Constrained decoding → 100% schema-valid for typed `Intent`. Schema correctness is non-negotiable; this is the only Groq model that guarantees it (alongside gpt-oss-20b). |
1568
+ | Tier 3 fallback | `openai/gpt-oss-20b` strict | Same constrained-decoding guarantee, cheaper, smaller. |
1569
+ | Tier 3 second fallback | `meta-llama/llama-4-scout-17b-16e-instruct` best-effort | Schema-aware but not strict; Zod-validate + 1 retry. |
1570
+ | Tier 3 final fallback | `llama-3.1-8b-instant` json_object | Last resort; cheapest; Zod-validate + retry. |
1571
+ | Synthesis (default + with-tables) | `llama-3.3-70b-versatile` | 86% MMLU, ~280 tps, $0.59/$0.79 per 1M. Schema strictness not required for narration. |
1572
+ | Reranker (Phase 4) | `llama-3.3-70b-versatile` | Same model as synthesis; LLM-rerank stays until Phase 11 swap to dedicated cross-encoder. |
1573
+ | CRAG critic (Phase 4) | `llama-3.3-70b-versatile` | Reuses same model; relevance scoring is a 0-1 numeric ask, not schema-strict. |
1574
+ | Multi-query rewriter (Phase 5b) | `llama-3.3-70b-versatile` | Generates 3 alternative phrasings; output validated via plain parse, not schema. |
1575
+ | RAGAS judge (Phase 0/10) | `llama-3.3-70b-versatile` | Numeric score output; ~$5 per full eval run. |
1576
+ | HyDE expansion (Phase 5b) | `llama-3.3-70b-versatile` | Existing model; rewrites short query as 2-3 sentence excerpt. |
1577
+
1578
+ **Cross-provider escape hatches (deferred to Phase 11):** OpenAI `gpt-4.1-mini` Structured Outputs (Tier 3 if Groq drift), Cohere Rerank 4.0 / Jina Reranker v3 / Voyage Rerank 2.5 / Zerank-2 / BGE self-host (reranker), Anthropic Haiku 4.5 (additional Tier 3 fallback). Each is a one-line provider-config change because the abstraction is wired upfront.
1579
+
1580
+ ### 13.5 Net delta v2.1 → v2.2
1581
+
1582
+ | Aspect | v2.1 | v2.2 | Why |
1583
+ |---|---|---|---|
1584
+ | M1 budget | ~5 days | ~8 days | RRF (+1d), CRAG gate (+0.5d), Phase 1 schema-strict pin (no extra time, just correct model), Phase 0 RAGAS (+0.25d), Phase 4 provider abstraction (+0.25d) |
1585
+ | M2 budget | ~4 days | ~6 days | Multi-query expansion (+1d in 5b), HippoRAG PPR (+1.5d in 5e), Phase 10 adaptive routing (+0.5d) |
1586
+ | M3 budget | (none) | ~1 day | New Phase 11 for deferred model swaps (cross-encoder reranker, optional cross-provider escape) |
1587
+ | **Total** | **~9 days** | **~14 days** | +5d for substantially higher retrieval quality (~62→~91% recall), agentic self-correction, multi-hop graph retrieval, removed single-vendor dependency on Groq 70B for reranking critical path |
1588
+ | Risk profile | Medium (intent fix only) | Lower (RAGAS gates catch regressions; CRAG masks retrieval edge cases) | RAGAS metrics are now merge gates; CRAG honest-downgrade prevents silent quality failures |
1589
+ | Provider lock-in | High (everything on Groq Llama) | Low (Groq stays default; provider abstraction enables Phase 11 swap config-only) | Phase 4 + Phase 1 wire abstractions even when not used immediately |
1590
+
1591
+ ### 13.6 Items still flagged for further verification
1592
+
1593
+ - RAGAS judge calibration on example-app fixtures — Llama 3.3 70B as judge is cheap but hasn't been calibrated against human ratings on example-app-specific data. First eval run will reveal whether scores are stable.
1594
+ - HippoRAG PPR performance on `entity_pairs` of example-app's actual size — verify edge count and required indices via `EXPLAIN` before Phase 5e build.
1595
+ - CRAG threshold (0.5) — placeholder. Tune from one week of shadow-mode telemetry.
1596
+ - Multi-query cost amplification — 4× retrieval calls per complex query. Measure actual cost lift vs RAGAS quality lift; abort if cost > 2× without quality gain.
1597
+ - Whether Groq `openai/gpt-oss-120b` strict mode actually achieves ~99.5% schema validity in production (one community report flagged "Structured Outputs ignored by openai/gpt-oss-120b"). Phase 1 shadow mode telemetry validates this empirically before Phase 5 cuts over.
1598
+
1599
+ ### 13.7 Updated risk profile
1600
+
1601
+ | Risk | v2.1 | v2.2 |
1602
+ |---|---|---|
1603
+ | Recall too low for multi-domain queries | High | Lower — RRF + multi-query + HippoRAG measured via RAGAS recall gate |
1604
+ | Synthesis hallucinates rows when table requested | Medium | Low — RAGAS faithfulness gate + plan principle "LLM narrates, never fabricates" |
1605
+ | Tier 3 returns malformed Intent JSON | Medium | Low — Groq `gpt-oss-120b` strict mode + Zod fallback |
1606
+ | Single-vendor Groq dependency on critical path | Medium | Low — provider abstraction wired in Phases 1/4; Phase 11 swap is config-only when needed |
1607
+ | Plan grows beyond user's appetite | Low (was 9d) | Low (now 14d, but reversible per-phase, M1 alone still solves visible bug) |
1608
+ | New dependency on dedicated reranker disrupts cutover | (planned in v2.1 thinking) | Eliminated — reranker swap deferred to Phase 11 last step |