Let live-routed answers reach the search page - #250
Merged
Merged
Conversation
Reported: the search page could not answer "what species are in reactome". Reproduced, with MCP configured: 0 tokens, state `nothing_found`. The chat answers the same question correctly. Those questions route to the live MCP lookup, which answers from tools and uses no retriever anywhere on the path. `astream_answer` opens its token boundary on `on_retriever_end`, so the boundary never opened, every token was discarded, and the caller was told nothing was found. Nothing could have caught it. It only happens where MCP is configured -- beta, not a developer's machine, where the same questions fall back to the vector store and retrieve normally. That is also why my own latency runs showed these questions answering: they were falling back. And the chat UI and the answer sweep both use `ainvoke`, which takes the final answer and never reads this stream, so the sweep passed 15/15 on beta while the endpoint was broken. The fix opens the boundary when preprocess ends with `active_sources` containing `live`. That is safe only on this path, and for a specific reason: the query expander lives inside the retriever, so with no retrieval there is nothing else streaming at the answer node to tell the answer apart from. The non-live path keeps waiting for retrieval, which a test pins. Verified both ways. With MCP: 36 and 10 tokens, `answered`. Without MCP: the fallback is unchanged, 1534 tokens with 12 citations. Live answers carry no citations, because no retriever ran. That is honest -- they come from live services rather than indexed pathways -- and callers already handle an empty citation list, which userguide answers made real. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
adamjohnwright
added a commit
that referenced
this pull request
Sep 18, 2026
Measured against beta: a well-formed but unknown token returns 404 as the OpenAPI says, but a malformed one returns 500, which the spec does not mention. FR-009 already calls a malformed token a normal negative outcome. Without this the implementation would have handled the two documented codes and let a malformed token become a failed state, or a retry loop against a service that will answer the same way every time. Found while adversarially reviewing PR #250, by probing the endpoint the contract depends on rather than trusting its documentation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
adamjohnwright
added a commit
that referenced
this pull request
Sep 18, 2026
Measured against beta: a well-formed but unknown token returns 404 as the OpenAPI says, but a malformed one returns 500, which the spec does not mention. FR-009 already calls a malformed token a normal negative outcome. Without this the implementation would have handled the two documented codes and let a malformed token become a failed state, or a retry loop against a service that will answer the same way every time. Found while adversarially reviewing PR #250, by probing the endpoint the contract depends on rather than trusting its documentation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reported by Adam: the search page could not answer "what species are in reactome". Reproduced with MCP configured — 0 tokens,
state: nothing_found— while the chat answers the same question correctly.Cause
Those questions route to the live MCP lookup, which answers from tools and uses no retriever anywhere on the path.
astream_answeropens its token boundary onon_retriever_end:So on the live path the boundary never opened, every token was discarded, and
answeredstayed false →nothing_found. The answer existed the whole time: 54 stream events, 224 characters, at the answer node.Why nothing caught it
Three independent reasons, which is why it reached production:
ainvoke, which takes the final answer and never reads this stream.ainvoke— so it passed 15/15 on beta while the endpoint was broken for the very questions it was testing.Fix
Open the boundary when
preprocessends withactive_sourcescontaininglive.That is safe on this path for a specific reason rather than by luck: the query expander lives inside the retriever, so where there is no retrieval there is nothing else streaming at the answer node to tell the answer apart from. The non-live path still waits for retrieval, and a test pins that it does.
Verified
answeredansweredansweredansweredThe regression test replays event sequences captured from a real run against beta's MCP sibling, so it needs no MCP to run — and it fails against the unfixed code.
Live answers carry no citations, because no retriever ran. That is honest — they come from live services rather than indexed pathways — and callers already handle an empty citation list, which userguide answers made real.
CI-equivalent locally: ruff, format, mypy, full suite with no API keys set.
🤖 Generated with Claude Code