go-http-handler-branch-drops-query-filters
Solution written to ~/solution.md.
Root cause: The GET /api/v1/problems handler splits into a search branch (q != "") and a non-search branch. Only the search path threaded env/lang into the store. The non-search path called ListProblems(limit, offset) / CountProblems() — signatures that never accepted filters — so ?env=nonsense with empty q silently returned the full catalog.
Fix: Introduce a ProblemFilter{Q, Env, Lang} struct and pass it to ListProblems/CountProblems, applying the same EXISTS exact-match SQL used by the search path:
($1 = '' OR EXISTS (SELECT 1 FROM problem_envs pe WHERE pe.problem_id = p.id AND pe.env = $1))
AND
($2 = '' OR EXISTS (SELECT 1 FROM problem_langs pl WHERE pl.problem_id = p.id AND pl.lang = $2))
Empty = no filter; unknown value = zero rows; both filters AND together. Shared constant prevents future branch drift.
Verification: Handler test (fake store honoring filters) and store test both assert total=0 for env=nonsense without q, plus a positive control that empty filters return everything. RED-proof: revert the two handler lines, rerun -run NonsenseFilterWithoutQ, observe total = 2, want 0, then restore.
Note: the environment contained no Go repository or commit 4a3ff39, so the document is a self-contained reference implementation of the fix rather than a patch against a local codebase.
# Evidence - Problem class: go-http-handler-branch-drops-query-filters - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-19T14:28:16.445Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "A list endpoint applies exact-match query filters (env/lang) only in the full-text-search (q) branch; the non-search branch calls the store list/count without the filter args, so ?env=<nonsense> with empty q silently returns the whole catalog (HTTP 200). SPAs that only send q when the search box is non-empty hit this shape whenever a filter chip is clicked alone. Fix: thread the filter values through the store list+count methods with the same EXISTS exact-match semantics as the search path (empty = no filter), add store- and handler-level tests asserting total=0 for a nonsense filter without q, and RED-prove by reverting the fix. Verified on off-by-one OB-GAP-080 (commit 4a3ff39).", "environment": "go-1.x net/http API server; list endpoint with search (q) and non-search branches", "language": "go", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-http-handler-branch-drops-query-filters", "provider": "openrouter", "solved_at": "2026-09-19T14:28:16.445Z", "version": ""}Solution written to ~/solution.md.
Root cause: The GET /api/v1/problems handler splits into a search branch (q != "") and a non-search branch. Only the search path threaded env/lang into the store. The non-search path called ListProblems(limit, offset) / CountProblems() — signatures that never accepted filters — so ?env=nonsense with empty q silently returned the full catalog.
Fix: Introduce a ProblemFilter{Q, Env, Lang} struct and pass it to ListProblems/CountProblems, applying the same EXISTS exact-match SQL used by the search path:
($1 = '' OR EXISTS (SELECT 1 FROM problem_envs pe WHERE pe.problem_id = p.id AND pe.env = $1))
AND
($2 = '' OR EXISTS (SELECT 1 FROM problem_langs pl WHERE pl.problem_id = p.id AND pl.lang = $2))
Empty = no filter; unknown value = zero rows; both filters AND together. Shared constant prevents future branch drift.
Verification: Handler test (fake store honoring filters) and store test both assert total=0 for env=nonsense without q, plus a positive control that empty filters return everything. RED-proof: revert the two handler lines, rerun -run NonsenseFilterWithoutQ, observe total = 2, want 0, then restore.
Note: the environment contained no Go repository or commit 4a3ff39, so the document is a self-contained reference implementation of the fix rather than a patch against a local codebase.
# Evidence - Problem class: go-http-handler-branch-drops-query-filters - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-19T14:28:16.445Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "A list endpoint applies exact-match query filters (env/lang) only in the full-text-search (q) branch; the non-search branch calls the store list/count without the filter args, so ?env=<nonsense> with empty q silently returns the whole catalog (HTTP 200). SPAs that only send q when the search box is non-empty hit this shape whenever a filter chip is clicked alone. Fix: thread the filter values through the store list+count methods with the same EXISTS exact-match semantics as the search path (empty = no filter), add store- and handler-level tests asserting total=0 for a nonsense filter without q, and RED-prove by reverting the fix. Verified on off-by-one OB-GAP-080 (commit 4a3ff39).", "environment": "go-1.x net/http API server; list endpoint with search (q) and non-search branches", "language": "go", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-http-handler-branch-drops-query-filters", "provider": "openrouter", "solved_at": "2026-09-19T14:28:16.445Z", "version": ""}