Skip to content

fix(chat): a question the planner splits keeps the chat's restrictions - #538

Merged
jwvanderstam merged 2 commits into
mainfrom
fix/multi-hop-keeps-source-scope
Nov 11, 2026
Merged

jwvanderstam merged 2 commits into
mainfrom
fix/multi-hop-keeps-source-scope

Conversation

@jwvanderstam

Copy link
Copy Markdown
Owner

What

ADR-6 recorded a live defect on the default chat path: get_rag_context_multi_hop took neither source_ids nor additional_workspace_ids. So a chat narrowed to chosen documents searched the whole workspace whenever the planner judged a ≥ 7-word question multi-hop, and a chat spanning additional workspaces silently lost them. Workspace isolation itself held — scope was always passed.

Found while writing the test: multi-hop citations also lacked knowledge_tier and low_relevance, so a global contribution in a multi-hop answer was cited as a workspace document (GKB-3 regression on that path).

Change

  • retrieve_contexts passes both restrictions to get_rag_context_multi_hop, which hands them to every sub-question's retrieve_context.
  • One _citation() helper builds a source entry for both paths; multi-hop adds its combined_score.
  • ADR-6's ⬜ item marked done; CHANGELOG Fixed; file-map row for the new test.

Tests

tests/unit/test_multi_hop_keeps_the_request_scope.py — 5 of its 6 tests fail on main; the sixth (the single-query path, as a control) passes both ways. The chat-related unit suites: 373 passed. ruff, mypy, lint-imports clean.

src/services/ is a security-critical path (CONTRIBUTING.md), so this gets the separate agent-session review before merging.

🤖 Generated with Claude Code

Multi-hop retrieval passed neither source_ids nor additional_workspace_ids to its
sub-questions, so a chat narrowed to chosen documents searched the whole workspace
whenever the planner judged a question multi-hop, and extra workspaces were silently
dropped (ADR-6, recorded as a live defect). Its citations also lacked knowledge_tier and
low_relevance, so a global contribution was cited as a workspace document. Both paths now
build citations with one helper. Workspace isolation itself was never affected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings November 10, 2026 23:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

…er sub-question

Answers the two Low findings of the #538 review: the tests now set
AGGREGATOR_AGENT_ENABLED and MCP_ENABLED off themselves, and one test asserts scope,
knowledge_scope, filename_filter, source_ids and additional_workspace_ids on every
sub-question call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings November 10, 2026 23:16

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

@jwvanderstam

Copy link
Copy Markdown
Owner Author

Separate agent-session review (CONTRIBUTING.md, QP-9)

Reviewed head: 8bc9c3a (base a04da4d). Diff only, with callers traced at the head.

No finding at Medium or above. The change narrows retrieval to what the caller already chose and was authorised for; it cannot widen what any caller reads.

Every sub-question's retrieve_context now gets the same five restrictions as the single-query path: scope, knowledge_scope, filename_filter, source_ids (new), additional_workspace_ids (new). Multi-hop citations come from the shared _citation() helper.

  • source_ids is only an ANY(%s) filter inside the scoped query, and a non-empty one disables the global-tier branch (retrieval.py:481), matching the single-query path.
  • additional_workspace_ids reach multi-hop only after _deny_additional → check_additional_workspace_access (api_routes.py:333), which refuses the whole request on one unauthorised id, before retrieval at :377. ChatService.contexts is the only caller of retrieve_contexts.
  • Global tier / API keys: check_knowledge_scope_access (api_routes.py:336) still refuses a non-local scope to a key before retrieval.
  • Threads: sub-questions run in a pool without the request contextvar, harmless since retrieve_context takes scope= explicitly.

Checklist

  1. Identity — not touched. 2. Authorisation — not touched; relies on the existing guards at :309 and :333, verified. 3. Scope — authorised scope on every sub-question, no ALL_WORKSPACES. 4. RLS — not touched. 5. Integrity — not touched. 6. Input — still via ChatRequest; no SQL. 7. Secrets — not touched. 8. Image — not touched. 9. Tests — 5 of 6 fail on the base, the sixth is a control; exact-value assertions, both members of each set, two sub-questions driven.

Findings

  • Low — the tests assert only source_ids and additional_workspace_ids per sub-question, not all five restrictions.
  • Low — the tests do not pin AGGREGATOR_AGENT_ENABLED / MCP_ENABLED, so a differently configured environment could make them fail misleadingly or the control pass vacuously.
  • Info — the aggregator path still drops the restrictions (ADR-6, latent while the flag is off).
  • Info — outside the diff: GraphRAG's related_names is unscoped on this base (R-12), and the query-cache key carries no scope (dead today).

🤖 Generated with Claude Code

@jwvanderstam

Copy link
Copy Markdown
Owner Author

Thanks for the review. Nothing at Medium or above.

🤖 Generated with Claude Code

@sonarqubecloud

Copy link
Copy Markdown

@jwvanderstam
jwvanderstam merged commit 643d9b5 into main Nov 11, 2026
17 checks passed
@jwvanderstam
jwvanderstam deleted the fix/multi-hop-keeps-source-scope branch November 11, 2026 00:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants