Skip to content

fix(agent): aggregator retrieval runs in the request's workspace - #409

Merged
jwvanderstam merged 4 commits into
mainfrom
fix/aggregator-request-scope
Oct 2, 2026
Merged

jwvanderstam merged 4 commits into
mainfrom
fix/aggregator-request-scope

Conversation

@jwvanderstam

Copy link
Copy Markdown
Owner

With AGGREGATOR_AGENT_ENABLED=true, chat retrieved nothing from the workspace's documents. Every local_docs job raised ScopeUnavailableError in its worker and came back empty, marked partial. It failed closed, so no other workspace's data was reachable, and the flag is off by default. I noted this in #405 and it's fixed here.

Two causes, and both were needed

  1. The route bound the scope too late. api_chat bound the request scope only inside _generate_sse, after retrieve_contexts had run. It now also binds it around retrieval, using the same workspace_id or ALL_WORKSPACES the stream already uses. run_in_threadpool copies the context into its thread.
  2. Worker threads didn't carry it. AggregatorAgent used a plain pool.submit, and contextvars don't follow it. Each job now runs in contextvars.copy_context().run.

Tests: each half is pinned separately

tests/unit/test_aggregator_carries_request_scope.py:

  • The aggregator's worker receives the bound workspace, for one job and for three parallel jobs.
  • End to end, POST /api/chat with the aggregator on retrieves in the caller's workspace.

Reproduced before the fix: retrieval was never reached (seen == []). With only the route fix reverted, the end-to-end test fails. With only the thread fix reverted, all three fail.

Verification

ruff, mypy, bandit: clean. Fast suite: 0 failed.

Merge note

This touches the same retrieve_contexts call in api_routes.py as #406 (workspace_id= → scope=). Whichever lands second needs a one-line conflict resolution, and I'll do it when I rebase #406.

🤖 Generated with Claude Code

jwvanderstam and others added 2 commits September 29, 2026 22:58
With AGGREGATOR_AGENT_ENABLED=true every local_docs job raised
ScopeUnavailableError and came back empty. Two causes, both needed:

- The chat route bound the request scope only inside the SSE generator,
  after retrieval had already run. It now binds it around retrieval too.
- AggregatorAgent submitted jobs to a ThreadPoolExecutor, which does not
  carry contextvars. Each job now runs in a copy of the caller's context.

Failed closed (no other workspace reachable); flag off by default. Each half
is pinned by its own test and fails with its fix reverted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves against P2-1b-ii (#406): retrieval now takes scope=, so the route binds
request_scope(scope) around the call and the test spies record the scope kwarg.

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

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 review overview

🟡 Changes recommended

The repaired aggregator path still ignores source, document, and additional-workspace retrieval constraints.

Review effort: Balanced
Findings: 1 Medium severity · 2 Low severity

Open (3)
What changed in this PR

Propagates authorized workspace scope through chat retrieval and aggregator worker threads.

Changes:

  • Binds scope during route-level retrieval.
  • Copies context into each aggregator worker.
  • Adds regression tests and documentation.
File Description
src/​routes_fastapi/​api_routes.py Binds request scope around retrieval.
src/​agent/​aggregator.py Copies context into worker threads.
tests/​unit/​test_aggregator_carries_request_scope.py Tests scope propagation.
CHANGELOG.md Documents the fix.
.claude/​rules/​file-map.md Registers the new test.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +352 to +354
plan=plan, scope=scope,
additional_workspace_ids=fields.get("additional_workspace_ids") or None,
source_ids=fields.get("active_source_ids") or None,
Comment thread CHANGELOG.md
Comment on lines +93 to +95
`ThreadPoolExecutor`, which does not carry contextvars. Every `local_docs` job raised
`ScopeUnavailableError` in its worker and came back empty, marked partial. It failed closed —
no other workspace's data was reachable — and the flag is off by default. The route now
Comment on lines +5 to +7
plain `pool.submit`, so with `AGGREGATOR_AGENT_ENABLED=true` every local-docs job raised
`ScopeUnavailableError` in its thread: fail-closed, nothing leaked, but retrieval came back
empty and marked partial on every request.
Copilot AI balanced review requested due to automatic review settings October 2, 2026 11:13

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 review overview

🔵 Needs a closer look

Aggregator retrieval still ignores authorized additional workspaces and active source restrictions.

Review effort: Balanced
Findings: 1 Medium severity · 2 Low severity

Open (3)

@jwvanderstam
jwvanderstam deleted the fix/aggregator-request-scope branch October 2, 2026 11:43
@jwvanderstam
jwvanderstam restored the fix/aggregator-request-scope branch October 2, 2026 11:43
@jwvanderstam jwvanderstam reopened this Oct 2, 2026
Copilot AI balanced review requested due to automatic review settings October 2, 2026 11:43

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 review overview

🟢 Approval recommended

The implementation correctly propagates scope and has focused regression coverage; comments only address minor documentation inaccuracies.

Review effort: Balanced
Findings: 1 Medium severity · 2 Low severity

Open (3)

@sonarqubecloud

sonarqubecloud Bot commented Oct 2, 2026

Copy link
Copy Markdown

@jwvanderstam
jwvanderstam merged commit 91e7cb4 into main Oct 2, 2026
16 checks passed
@jwvanderstam
jwvanderstam deleted the fix/aggregator-request-scope branch October 2, 2026 12:18
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