ask-query-system
37 beliefs (34 IN, 3 OUT)
The ask query system is the primary interface for retrieving and synthesizing knowledge from the TMS belief network. It supports tiered query modes (ask-has-tiered-query-modes): full LLM synthesis with a bounded tool loop, a no-synth mode that bypasses the LLM entirely and returns raw FTS5 search results (ask-no-synth-bypasses-llm, no-synth-mode-skips-llm), a dual mode that combines TMS and source-document retrieval with up to three LLM calls (ask-dual-mode-makes-three-llm-calls, ask-dual-requires-sources-db), and a natural mode that strips belief metadata and citation instructions for plain-language output (ask-natural-mode-strips-metadata-and-cite). The system invokes the Claude CLI in pipe mode rather than the native tool-use API, so tool calls are parsed from JSON lines in the text output (ask-uses-text-based-tool-protocol).
The defining design principle is fault tolerance with bounded execution (ask-is-fault-tolerant-and-bounded). The function guarantees a string return on every code path and never raises exceptions to the caller (ask-always-returns-string, ask-never-raises-on-llm-failure). When LLM synthesis fails due to timeout, missing binary, or non-zero exit, the system falls back to raw FTS5 search results (ask-falls-back-to-raw-search). The tool-call loop is capped at three iterations without MCP servers (ask-tool-loop-capped-at-three) and five iterations with MCP servers (ask-mcp-iteration-limit-is-five), with a final-turn instruction appended on the last round to force an answer. Source document content persists across all tool-call round-trips so the LLM retains context throughout the multi-turn loop (ask-source-chunks-persist-across-tool-iterations). The LLM invocation also strips the CLAUDECODE environment variable to prevent recursive invocation (ask-strips-claudecode-env).
The FTS5 search layer has its own resilience mechanisms. Query errors are silently caught and fall back to substring matching (fts-errors-silently-caught-in-search). When a multi-term query returns no results, the engine progressively drops terms via combinations to find partial matches (fts-progressive-relaxation), but this relaxation is bounded at 50 queries to prevent combinatorial explosion (fts-relaxation-capped-at-fifty-queries, fts-relaxation-budget-caps-at-50). Stop words are filtered before querying, with a fallback to all words longer than one character if every term is a stop word (fts-stop-words-filtered-before-query, ask-stop-word-fallback-ensures-nonempty-query). The sources database layer similarly degrades gracefully, catching OperationalError and DatabaseError to return empty results rather than crashing (ask-sources-db-failure-silently-degrades).
MCP integration adds an optional layer for external tool access. The MCP bridge runs a dedicated asyncio event loop on a daemon thread (mcp-bridge-runs-dedicated-event-loop-thread) with timeout bounds at both connection (30s) and per-tool execution (60s) phases (mcp-bridge-is-timeout-bounded-at-all-phases, mcp-bridge-connect-blocks-up-to-30s, mcp-bridge-call-tool-timeout-60s). MCP errors during the ask loop are caught and fed back to the LLM as context rather than propagated (ask-mcp-errors-non-fatal), creating defense-in-depth bounding at both application and transport layers (ask-mcp-is-defense-in-depth-bounded). The mcp package itself is an optional dependency guarded by try/except at import time (mcp-is-optional-dependency). Two beliefs are currently retracted: one claiming the tool catalog always reflects current server capabilities (ask-mcp-tool-use-has-current-catalog) and a similar claim about accurate tool discovery (ask-mcp-achieves-accurate-bounded-tool-use). This is consistent with the active belief that the tool catalog is snapshot-at-connect and never refreshed (mcp-bridge-tools-snapshot-at-connect), meaning the catalog can go stale if an MCP server adds tools after the initial handshake. A third retracted belief (ask-degrades-across-all-external-dependencies) made a broader claim about graceful degradation across all three external dependencies; its retraction may reflect a refinement where the individual degradation behaviors are tracked by more specific beliefs rather than a single sweeping claim.
-
IN
ask-agentic-loop-is-bounded
The tool-call loop in `ask()` has a maximum iteration count; an LLM that perpetually requests more searches is terminated and the raw response is returned. -
IN
ask-always-returns-string
`ask()` returns a string on every code path — LLM response, raw search results, or fallback; it never raises an exception to the caller. -
OUT
ask-degrades-across-all-external-dependencies
The ask module degrades gracefully across all three external dependencies: LLM binary (catches TimeoutExpired/RuntimeError, falls back to raw search), FTS5 index (self-healing derived index with substring fallback), and source chunks database (catches OperationalError/DatabaseError and returns empty results) — no single external system failure prevents useful query responses. -
IN
ask-dual-mode-makes-three-llm-calls
Dual mode makes up to 3 LLM calls (1 TMS synthesis + 1 FTS RAG + 1 merge), short-circuiting to 2 if either retrieval path returns empty. -
IN
ask-dual-requires-sources-db
Calling `ask(..., dual=True)` without providing a `sources_db` path raises `ValueError`. -
IN
ask-falls-back-to-raw-search
When LLM synthesis fails for any reason (timeout, missing CLI, non-zero exit), `ask()` returns the raw FTS5 search results as fallback. -
IN
ask-has-tiered-query-modes
The ask module supports tiered query modes with graceful degradation: full LLM synthesis with a bounded 3-iteration tool loop, no-synth mode that bypasses the LLM entirely, and automatic fallback from LLM failure to raw FTS5 search results. -
IN
ask-is-fault-tolerant-and-bounded
The ask module is fault-tolerant (always returns a string, catches LLM failures, falls back to raw FTS5 search) and execution-bounded (tool loop capped at 3 iterations), ensuring reliable bounded knowledge retrieval regardless of LLM availability. -
OUT
ask-mcp-achieves-accurate-bounded-tool-use
MCP tool integration in ask() achieves both bounded safety (iteration caps, error tolerance, transport timeouts at two layers) and accurate tool discovery (catalog reflects current server capabilities rather than a stale snapshot). -
IN
ask-mcp-errors-non-fatal
When an MCP bridge's `call_tool` raises an exception during the ask loop, `ask()` catches the error, feeds it back to the LLM as context, and continues the tool loop rather than propagating to the caller. -
IN
ask-mcp-integration-is-safely-bounded
MCP tool calls in `ask()` are both error-tolerant (exceptions caught and fed back as context for alternative tool selection) and iteration-bounded (5 tool-call rounds max), preventing both crashes from MCP server failures and runaway tool loops. -
IN
ask-mcp-is-defense-in-depth-bounded
MCP-backed ask queries are bounded at two independent layers: application-level iteration caps with error-tolerant fallback at the ask layer, plus per-call and connection timeouts at the MCP bridge transport layer — no single timeout failure can cause unbounded execution. -
IN
ask-mcp-iteration-limit-is-five
When `mcp_servers` is non-empty, `ask()` allows up to 5 tool-call iterations before forcing a final LLM response (6 total invocations), compared to the lower limit without MCP servers. -
OUT
ask-mcp-tool-use-has-current-catalog
Ask's MCP tool integration achieves full reliability — errors caught, iterations bounded — with the tool catalog always reflecting the MCP server's current capabilities rather than a stale connection-time snapshot. -
IN
ask-natural-mode-strips-metadata-and-cite
When `natural=True`, all belief metadata (`**Status:**`, `### ` headers, `**Source:**`) is stripped from the prompt context and the "Cite belief IDs" instruction is replaced with "plain natural language". -
IN
ask-never-raises-on-llm-failure
`ask()` catches `TimeoutExpired` and `RuntimeError` from `_invoke_claude()` and returns raw FTS5 search results instead of propagating the exception — the function guarantees a string return. -
IN
ask-no-synth-bypasses-llm
When `no_synth=True`, `ask()` returns raw `api.search()` results without invoking the Claude CLI. -
IN
ask-prompt-no-template-placeholders
`build_ask_prompt` never leaves `{{` or `}}` template markers in the generated prompt string — all placeholders are resolved before output. -
IN
ask-source-chunks-persist-across-tool-iterations
Source document content included in the initial ask prompt is re-included in all subsequent prompts after tool-call round-trips, ensuring the LLM retains source context across the entire multi-turn loop. -
IN
ask-sources-db-failure-silently-degrades
If the `sources_db` SQLite file is missing or corrupt, `_search_source_chunks` catches `OperationalError`/`DatabaseError` and returns empty string, degrading to belief-only mode without user-visible errors. -
IN
ask-stop-word-fallback-ensures-nonempty-query
`_search_source_chunks` strips stop words from the question before building the FTS5 query, but falls back to all words longer than 1 character if every word is a stop word — ensuring the query is never empty -
IN
ask-strips-claudecode-env
`_invoke_claude` removes the `CLAUDECODE` environment variable to prevent recursive invocation when running inside Claude Code. -
IN
ask-tool-loop-capped-at-three
The LLM synthesis loop runs at most `MAX_ITERATIONS` (3) rounds, with `FINAL_TURN_INSTRUCTION` appended on the last iteration to force a final answer. -
IN
ask-uses-text-based-tool-protocol
`ask.py` implements tool use by parsing JSON lines with a `"tool"` key from LLM text output, not Claude's native tool-use API, because it invokes `claude -p` (pipe mode) which doesn't support function calling. -
IN
fts-errors-silently-caught-in-search
FTS5 query errors in `_fts_search` are silently caught and return an empty list, falling back to substring matching — the only place in the API where errors are deliberately swallowed (FTS5 table may not exist). -
IN
fts-progressive-relaxation
When a multi-term FTS query returns no results, the search engine progressively drops terms until it finds matches or exhausts all subsets. -
IN
fts-relaxation-bounded
Progressive FTS query relaxation is bounded: a 20-term query produces at most 51 `_fts_query` invocations, preventing unbounded search expansion on long input queries. -
IN
fts-relaxation-budget-caps-at-50
Progressive FTS5 search relaxation — dropping query terms via combinations when the full-term query returns no results — is capped at `_MAX_RELAXATION_QUERIES` (50) to prevent combinatorial blowup on many-term queries. -
IN
fts-relaxation-capped-at-fifty-queries
`_fts_search` caps progressive term relaxation at 50 FTS5 queries to prevent combinatorial explosion on long search inputs, dropping terms one at a time via `combinations` down to `len(terms) // 2`. -
IN
fts-stop-words-filtered-before-query
FTS queries filter a `_STOP_WORDS` frozenset before querying FTS5; if all terms are stop words, the search falls back to terms longer than 1 character. -
IN
mcp-bridge-call-tool-timeout-60s
`call_tool()` blocks for up to 60 seconds per invocation via `future.result(timeout=60)`; if the MCP server hangs, the calling thread unblocks with `TimeoutError`. -
IN
mcp-bridge-connect-blocks-up-to-30s
`connect()` blocks the calling thread for up to 30 seconds waiting for MCP session initialization via `_ready.wait(timeout=30)`, then raises `TimeoutError` if the server doesn't respond. -
IN
mcp-bridge-is-timeout-bounded-at-all-phases
MCP bridge operations are timeout-bounded at both connection establishment (30s) and per-tool execution (60s), preventing indefinite blocking on unresponsive MCP servers at any lifecycle phase. -
IN
mcp-bridge-runs-dedicated-event-loop-thread
Each `McpBridge` instance runs its own asyncio event loop on a daemon thread; the MCP session stays alive until `close()` signals the shutdown event, bridging the sync/async boundary via `run_coroutine_threadsafe`. -
IN
mcp-bridge-tools-snapshot-at-connect
The tool catalog and server instructions are populated once during `connect()` and never refreshed — there is no mechanism to pick up tools added after the initial handshake. -
IN
mcp-is-optional-dependency
The `mcp` package is guarded by try/except at import time; `_require_mcp()` defers the `ImportError` to `McpBridge` construction so the module can be imported unconditionally without the SDK installed. -
IN
no-synth-mode-skips-llm
When `no_synth=True`, `ask()` returns formatted FTS5 search results without invoking `_invoke_claude()` — the LLM is never contacted.