Skip to content

fix(backend): harden Copilot session tenancy - #13650

Open
ntindle wants to merge 5 commits into
devfrom
fix/session-org-membership
Open

fix(backend): harden Copilot session tenancy#13650
ntindle wants to merge 5 commits into
devfrom
fix/session-org-membership

Conversation

@ntindle

@ntindle ntindle commented Jul 22, 2026

Copy link
Copy Markdown
Member

Why / What / How

A Copilot session persists its organization and team, but membership can change after session creation. Without a final DB-backed check at every admission and execution boundary, an offboarded user can keep using queued, resumed, scheduled, or internally injected turns under stale organization billing and permissions.

This PR makes the persisted session organization authoritative and revalidates active membership before work can reach either Copilot engine. Organization revocation hard-denies. Team removal within an active organization rehomes new turns to organization-home; it does not silently move already-running work whose capabilities may have been established under the former team.

Changes 🏗️

  • Add shared DB/RPC-backed session-tenancy resolution.
  • Gate HTTP stream and pending-message admission, queue selection, and the final processor path before provider leases or SDK/baseline execution.
  • Revalidate scheduled Copilot followups before session creation or dispatch.
  • Cover internal run_sub_session and AutoPilot pending-message injection with the existing turn-in-flight compare-and-set boundary.
  • Keep active stale-team and legacy busy-session injection fail closed with a retryable conflict instead of changing tenancy mid-turn.
  • Prevent typed admission/authorization failures from being republished by generic AutoPilot recovery; preserve validated tenancy and permission ceilings for genuine recovery.
  • Keep legacy untagged new-turn behavior while clearing sticky runtime tenancy.
  • Regenerate and verify OpenAPI documentation for the new error responses.

Product behavior:

  • Removed, suspended, or soft-deleted organization membership: hard denial.
  • Active organization with stale team: new turn runs from organization-home.
  • Busy turn with stale or unresolved tenancy: no pending injection; retry after the active turn.
  • Cron followups retry on the next tick; a due one-shot follows existing DateTrigger consumption behavior if membership lookup fails.

Checklist 📋

For code changes:

  • I have clearly listed my changes in the PR description
  • I have made a test plan
  • I have tested my changes according to the test plan:
    • Whole backend formatter and lint
    • Full Pyright: zero errors
    • Processor and turn queue: 60 passed
    • Route tenancy/pending harness: 12 passed
    • Session waiter: 8 passed
    • AutoPilot recovery: 8 passed
    • Copilot permissions: 18 passed
    • Scheduler: 51 passed
    • Schedule/sub-session: 34 passed
    • Frontend TypeScript
    • OpenAPI export/format reproduced byte-identically; Orval produced no client diff
    • Independent frozen-scope audit completed
Example test plan
  • Create from scratch and execute an agent with at least 3 blocks
  • Import an agent from file upload, and confirm it executes correctly
  • Upload agent to marketplace
  • Import an agent from marketplace and confirm it executes correctly
  • Edit an agent from monitor, and confirm it executes correctly

For configuration changes:

  • .env.default is updated or already compatible with my changes
  • docker-compose.yml is updated or already compatible with my changes
  • No configuration changes are required
Examples of configuration changes
  • Changing ports
  • Adding new services that need to communicate with each other
  • Secrets or environment variable changes
  • New or infrastructure changes such as databases

Note

High Risk
Touches authorization and tenancy across chat dispatch, queue promotion, executor admission, and scheduled turns—security-critical paths that control org-scoped spend and agent execution.

Overview
Closes SECRT-2489 by re-verifying a chat session's persisted org/team on every turn, instead of trusting membership only at session creation.

Adds shared resolve_session_tenancy policy: org revocation is a hard deny (HTTP 403 / fail-closed elsewhere); stale team membership is soft-stripped to org-home. Wires that check across HTTP /stream and pending-message handlers, queue promotion, the CoPilot processor (authoritative DB metadata before execution), sub-session/AutoPilot admission, and scheduled followups.

Also propagates resolved organization_id/team_id through turn enqueue/recovery paths, rejects unverifiable pending injection into in-flight turns (409), and drops revoked queued sessions so they cannot block later valid work.

Reviewed by Cursor Bugbot for commit d1632a8. Bugbot is set up for automated code reviews on this repo. Configure here.

…-2489)

The persisted ChatSession org/team was trusted for every turn, but
membership is only validated at session creation. A user removed or
suspended from an org afterward kept acting under the stale org through
existing sessions — spending credits and running agents under a tenancy
they no longer belonged to.

Re-verify ACTIVE membership at the turn-dispatch choke points:

- New backend/copilot/session_tenancy.py::verify_session_org_membership
  runs up to two indexed lookups on the membership unique constraints
  (OrgMember@@unique(orgId,userId); TeamMember@@unique(teamId,userId)),
  mirroring get_request_context's checks for the header org.
- HTTP /stream (stream_chat_post): no ACTIVE OrgMember for the session's
  org → 403 (honest failure, not silent degradation to personal). A stale
  team on a still-valid org is stripped to org-home.
- Queue promotion (dispatch_next_for_user): re-check before promoting a
  turn that was queued while the user was a member; on revocation drop the
  session out of the queue (queued → idle) so it neither runs under the
  revoked org nor blocks the user's other queued turns.

Personal/ctx-fallback sessions (organization_id None) are unaffected.

Co-Authored-By: Claude Opus <noreply@anthropic.com>
@ntindle
ntindle requested a review from a team as a code owner July 22, 2026 17:40
@ntindle
ntindle requested review from 0ubbe and kcze and removed request for a team July 22, 2026 17:40
@github-project-automation github-project-automation Bot moved this to 🆕 Needs initial review in AutoGPT development kanban Jul 22, 2026
@ntindle

ntindle commented Jul 22, 2026

Copy link
Copy Markdown
Member Author

/batch

@github-actions github-actions Bot added platform/backend AutoGPT Platform - Back end size/xl labels Jul 22, 2026
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Chat routes, execution paths, scheduled turns, and queued turns now revalidate organization and team membership. Revoked organization access blocks dispatch. Stale team access falls back to the organization context. Tenancy metadata propagates through streaming, scheduling, recovery, and sub-session flows.

Changes

Session tenancy revalidation

Layer / File(s) Summary
Membership verification contract
autogpt_platform/backend/backend/api/features/orgs/*, autogpt_platform/backend/backend/copilot/session_tenancy*, autogpt_platform/backend/backend/data/db_manager.py
Adds boolean organization and team membership checks. Active teams retain their ID. Invalid teams resolve to the organization context. Revoked organizations raise SessionOrgMembershipRevoked.
Chat route tenancy gates
autogpt_platform/backend/backend/api/features/chat/routes.py, autogpt_platform/backend/backend/api/features/chat/routes_test.py, autogpt_platform/frontend/src/app/api/openapi.json
Streaming and pending-message routes revalidate tenancy. They return documented 403, 409, and 503 responses for revoked, changed, or unavailable access.
Execution and admission authorization
autogpt_platform/backend/backend/copilot/executor/*, autogpt_platform/backend/backend/copilot/sdk/session_waiter*
Execution validates persisted session ownership and tenancy before streaming or message injection. Admission failures prevent unauthorized queueing.
Queued turn promotion gate
autogpt_platform/backend/backend/copilot/turn_queue*
Queued sessions persist tenancy and revalidate it during FIFO promotion. Revoked sessions return to idle, while valid sessions continue dispatch.
Scheduled and autopilot tenancy propagation
autogpt_platform/backend/backend/executor/*, autogpt_platform/backend/backend/blocks/*, autogpt_platform/backend/backend/copilot/tools/*
Scheduled, recovery, follow-up, sub-session, and autopilot turns propagate resolved organization and team context. Terminal admission failures do not enqueue recovery turns.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ChatClient
  participant ChatRoutes
  participant resolve_session_tenancy
  participant TurnQueue
  participant Scheduler
  ChatClient->>ChatRoutes: submit stream or pending-message request
  ChatRoutes->>resolve_session_tenancy: revalidate session tenancy
  resolve_session_tenancy-->>ChatRoutes: resolved tenancy or access error
  ChatRoutes-->>ChatClient: continue or return documented error
  TurnQueue->>resolve_session_tenancy: revalidate queued session
  resolve_session_tenancy-->>TurnQueue: resolved team or revoked organization
  TurnQueue->>TurnQueue: reset revoked session or select next valid session
  Scheduler->>resolve_session_tenancy: resolve scheduled session tenancy
  resolve_session_tenancy-->>Scheduler: resolved organization and team
  Scheduler->>TurnQueue: schedule turn with resolved tenancy
Loading

Possibly related PRs

Suggested reviewers: 0ubbe, majdyz

Poem

A rabbit checks the org gate twice,
And trims a stale team’s trail.
Revoked turns leave the queue,
Valid sessions set sail.
Tenancy follows every hop.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 67.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: hardening backend Copilot session tenancy.
Description check ✅ Passed The description directly explains the session-tenancy security changes, affected paths, product behavior, and test coverage.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/session-org-membership

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread autogpt_platform/backend/backend/copilot/turn_queue.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
autogpt_platform/backend/backend/copilot/session_tenancy_test.py (1)

20-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the exact Prisma lookup keys.

The mocks return fixed values regardless of their arguments, so a regression using the wrong (orgId, userId) or (teamId, userId) pair—or skipping the active-team lookup—would still pass. Add assert_awaited_once_with(...) checks for the unique lookup payloads, especially in the active-team test.

Proposed test strengthening
-    _patch_prisma(
+    prisma = _patch_prisma(
         mocker,
         org_member=_member(OrgMemberStatus.ACTIVE),
         team_member=_member(OrgMemberStatus.ACTIVE),
     )
     result = await verify_session_org_membership(
         user_id="u1", organization_id="org-1", team_id="team-1"
     )
     assert result == "team-1"
+    prisma.orgmember.find_unique.assert_awaited_once_with(
+        where={"orgId_userId": {"orgId": "org-1", "userId": "u1"}}
+    )
+    prisma.teammember.find_unique.assert_awaited_once_with(
+        where={"teamId_userId": {"teamId": "team-1", "userId": "u1"}}
+    )

Also applies to: 32-114

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@autogpt_platform/backend/backend/copilot/session_tenancy_test.py` around
lines 20 - 24, Strengthen the Prisma mocks in the session tenancy tests by
asserting each orgmember.find_unique and teammember.find_unique AsyncMock was
awaited exactly once with the expected (orgId, userId) or (teamId, userId)
lookup payload. Apply these assertions across the affected tests, with
particular attention to the active-team scenario to verify the active-team
lookup is performed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@autogpt_platform/backend/backend/copilot/turn_queue.py`:
- Around line 307-325: In dispatch_next_for_user, after dropping the revoked
head session to idle and invalidating its cache, invoke
dispatch_next_for_user(user_id) again so the next queued session can be
promoted. Replace the immediate False return while preserving the existing
status update and cache invalidation.

---

Nitpick comments:
In `@autogpt_platform/backend/backend/copilot/session_tenancy_test.py`:
- Around line 20-24: Strengthen the Prisma mocks in the session tenancy tests by
asserting each orgmember.find_unique and teammember.find_unique AsyncMock was
awaited exactly once with the expected (orgId, userId) or (teamId, userId)
lookup payload. Apply these assertions across the affected tests, with
particular attention to the active-team scenario to verify the active-team
lookup is performed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3bdd838e-c0fc-4059-a348-ff59a8c921cd

📥 Commits

Reviewing files that changed from the base of the PR and between 688a68c and a7a6cb6.

📒 Files selected for processing (6)
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
📜 Review details
⏰ Context from checks skipped due to timeout. (13)
  • GitHub Check: check API types
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.11)
  • GitHub Check: type-check (3.12)
  • GitHub Check: type-check (3.11)
  • GitHub Check: type-check (3.13)
  • GitHub Check: lint
  • GitHub Check: types
  • GitHub Check: lint
  • GitHub Check: Analyze (typescript)
  • GitHub Check: Check PR Status
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (5)
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

autogpt_platform/backend/**/*.py: Use poetry run ... command for executing Python package dependencies
Use top-level imports only — avoid local/inner imports except for lazy imports of heavy optional dependencies like openpyxl
Use absolute imports with from backend.module import ... for cross-package imports; single-dot relative imports are acceptable for sibling modules within the same package; avoid double-dot relative imports
Do not use duck typing — avoid hasattr/getattr/isinstance for type dispatch; use typed interfaces/unions/protocols instead
Use Pydantic models over dataclass/namedtuple/dict for structured data
Do not use linter suppressors — no # type: ignore, # noqa, # pyright: ignore; fix the type/code instead
Prefer list comprehensions over manual loop-and-append patterns
Use early return with guard clauses first to avoid deep nesting
Use %s for deferred interpolation in debug log statements for efficiency; use f-strings elsewhere for readability (e.g., logger.debug("Processing %s items", count) vs logger.info(f"Processing {count} items"))
Sanitize error paths by using os.path.basename() in error messages to avoid leaking directory structure
Be aware of TOCTOU (Time-Of-Check-Time-Of-Use) issues — avoid check-then-act patterns for file access and credit charging
Use transaction=True for Redis pipelines to ensure atomicity on multi-step operations
Use max(0, value) guards for computed values that should never be negative
Keep files under ~300 lines; if a file grows beyond this, split by responsibility (extract helpers, models, or a sub-module into a new file)
Keep functions under ~40 lines; extract named helpers when a function grows longer
...

Files:

  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
autogpt_platform/{backend,autogpt_libs}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
autogpt_platform/backend/backend/api/features/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Update routes in '/backend/backend/api/features/' and add/update Pydantic models in the same directory for API development

Files:

  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
autogpt_platform/backend/**/api/**/*.py

📄 CodeRabbit inference engine (autogpt_platform/backend/AGENTS.md)

autogpt_platform/backend/**/api/**/*.py: Use Security() instead of Depends() for authentication dependencies to get proper OpenAPI security specification
Follow SSE (Server-Sent Events) protocol: use data: lines for frontend-parsed events (must match Zod schema) and : comment lines for heartbeats/status

Files:

  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
autogpt_platform/backend/**/*_test.py

📄 CodeRabbit inference engine (autogpt_platform/backend/AGENTS.md)

autogpt_platform/backend/**/*_test.py: Use pytest with snapshot testing for API responses
Colocate test files with source files using *_test.py naming convention
Mock at boundaries — mock where the symbol is used, not where it's defined; after refactoring, update mock targets to match new module paths
Use AsyncMock from unittest.mock for async functions in tests
When writing tests, use Test-Driven Development (TDD): write failing tests marked with @pytest.mark.xfail before implementation, then remove the marker once the implementation is complete
When creating snapshots in tests, use poetry run pytest path/to/test.py --snapshot-update; always review snapshot changes with git diff before committing

Files:

  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
🧠 Learnings (14)
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
📚 Learning: 2026-03-04T08:04:35.881Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12273
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:216-220
Timestamp: 2026-03-04T08:04:35.881Z
Learning: In the AutoGPT Copilot backend, ensure that SVG images are not treated as vision image types by excluding 'image/svg+xml' from INLINEABLE_MIME_TYPES and MULTIMODAL_TYPES in tool_adapter.py; the Claude API supports PNG, JPEG, GIF, and WebP for vision. SVGs (XML text) should be handled via the text path instead, not the vision path.

Applied to files:

  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
📚 Learning: 2026-04-01T04:17:41.600Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12632
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:0-0
Timestamp: 2026-04-01T04:17:41.600Z
Learning: When reviewing AutoGPT Copilot tool implementations, accept that `readOnlyHint=True` (provided via `ToolAnnotations`) may be applied unconditionally to *all* tools—even tools that have side effects (e.g., `bash_exec`, `write_workspace_file`, or other write/save operations). Do **not** flag these tools for having `readOnlyHint=True`; this is intentional to enable fully-parallel dispatch by the Anthropic SDK/CLI and has been E2E validated. Only flag `readOnlyHint` issues if they conflict with the established `ToolAnnotations` behavior (e.g., missing/incorrect propagation relative to the intended annotation mechanism).

Applied to files:

  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
📚 Learning: 2026-06-06T12:22:37.648Z
Learnt from: anvyle
Repo: Significant-Gravitas/AutoGPT PR: 13302
File: autogpt_platform/backend/backend/copilot/tools/agent_search.py:579-583
Timestamp: 2026-06-06T12:22:37.648Z
Learning: When writing LLM-facing instruction strings that trigger tool calls in this AutoGPT codebase, use the exact registered tool name `view_agent_output` (as defined in `backend/copilot/tools/agent_output.py` via its `name` property and exported via `TOOL_REGISTRY`). Do not reference the bare name `agent_output`, since it is not a valid tool name and will cause tool invocation to fail.

Applied to files:

  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
📚 Learning: 2026-03-05T15:42:08.207Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.

Applied to files:

  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
📚 Learning: 2026-03-16T16:35:40.236Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12440
File: autogpt_platform/backend/backend/api/features/workflow_import.py:54-63
Timestamp: 2026-03-16T16:35:40.236Z
Learning: Avoid using the word 'competitor' in public-facing identifiers and text. Use neutral naming for API paths, model names, function names, and UI text. Examples: rename 'CompetitorFormat' to 'SourcePlatform', 'convert_competitor_workflow' to 'convert_workflow', '/competitor-workflow' to '/workflow'. Apply this guideline to files under autogpt_platform/backend and autogpt_platform/frontend.

Applied to files:

  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
📚 Learning: 2026-03-31T15:37:38.626Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12623
File: autogpt_platform/backend/backend/copilot/tools/agent_generator/fixer.py:37-47
Timestamp: 2026-03-31T15:37:38.626Z
Learning: When validating/constructing Anthropic API model IDs in Significant-Gravitas/AutoGPT, allow the hyphen-separated Claude Opus 4.6 model ID `claude-opus-4-6` (it corresponds to `LlmModel.CLAUDE_4_6_OPUS` in `autogpt_platform/backend/backend/blocks/llm.py`). Do NOT require the dot-separated form in Anthropic contexts. Only OpenRouter routing variants should use the dot separator (e.g., `anthropic/claude-opus-4.6`); `claude-opus-4-6` should be treated as correct when passed to Anthropic, and flagged only if it’s used in the OpenRouter path where the dot form is expected.

Applied to files:

  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
📚 Learning: 2026-04-15T02:43:36.890Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12780
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:0-0
Timestamp: 2026-04-15T02:43:36.890Z
Learning: When reviewing Python exception handlers, do not flag `isinstance(e, X)` checks as dead/unreachable if the caught exception `X` is a subclass of the exception type being handled. For example, if `X` (e.g., `VirusScanError`) inherits from `ValueError` (directly or via an intermediate class) and it can be raised within an `except ValueError:` block, then `isinstance(e, X)` inside that handler is reachable and should not be treated as dead code.

Applied to files:

  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
📚 Learning: 2026-05-23T05:29:43.085Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 13200
File: autogpt_platform/backend/backend/executor/scheduler.py:590-593
Timestamp: 2026-05-23T05:29:43.085Z
Learning: When reviewing Python code that uses Pydantic discriminated/tagged unions (e.g., `Annotated[Union[...], Field(discriminator="kind")]`), recognize that using `isinstance(x, SomeVariantInfo)` to narrow the union is an intentional and correct runtime guard and should also enable static type narrowing in tools like Pyright. Do not recommend replacing such `isinstance`-based narrowing with `cast(...)` when the check already proves the variant at runtime.

Applied to files:

  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
📚 Learning: 2026-04-22T11:46:04.431Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/config.py:0-0
Timestamp: 2026-04-22T11:46:04.431Z
Learning: Do not flag the Claude Sonnet 4.6 model ID as incorrect when it uses the project’s established hyphenated convention: `anthropic/claude-sonnet-4-6`. This hyphen form is the intentional, production convention and should be treated as valid (including in files like llm.py, blocks tests, reasoning.py, `_is_anthropic_model` tests, and config defaults). Note that OpenRouter also accepts the dot variant `anthropic/claude-sonnet-4.6`, so either form may be tolerated, but `anthropic/claude-sonnet-4-6` should be considered the standard to match project usage.

Applied to files:

  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
📚 Learning: 2026-04-22T11:46:12.892Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/baseline/service.py:322-332
Timestamp: 2026-04-22T11:46:12.892Z
Learning: In this codebase (Significant-Gravitas/AutoGPT), OpenRouter-routed Anthropic model IDs should use the hyphen-separated convention (e.g., `anthropic/claude-sonnet-4-6`, `anthropic/claude-opus-4-6`). Although OpenRouter may accept both hyphen and dot variants, treat the hyphen-separated form as the intended, correct codebase-wide convention and do not flag it as an error. Only flag the dot-separated variant (e.g., `anthropic/claude-sonnet-4.6`) as incorrect when reviewing/validating model ID strings for OpenRouter-routed Anthropic models.

Applied to files:

  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
📚 Learning: 2026-05-07T18:48:14.242Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 13040
File: autogpt_platform/backend/backend/blocks/llm.py:0-0
Timestamp: 2026-05-07T18:48:14.242Z
Learning: In this repository, isort may split imports from the same module into separate blocks when some imports are aliased (e.g., `from module import X as Y`) and others are not. Preserve the two-block layout when it results from isort (such as keeping `from openai.types.chat import ChatCompletion as OpenAIChatCompletion` separate from non-aliased imports from `openai.types.chat`). Do not treat that split as a style issue during review; merging them into a single block can fail CI with `Imports are incorrectly sorted and/or formatted`.

Applied to files:

  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
📚 Learning: 2026-05-26T14:24:34.866Z
Learnt from: Abhi1992002
Repo: Significant-Gravitas/AutoGPT PR: 13217
File: autogpt_platform/backend/backend/api/features/search/service.py:137-137
Timestamp: 2026-05-26T14:24:34.866Z
Learning: In the Significant-Gravitas/AutoGPT backend, treat `user_id` (an opaque UUID used only for correlation/tracing) as non-PII. Do not flag direct logging of `user_id` in `logger.warning`/`logger.info` statements as a PII exposure issue, as the established convention is to log `user_id` for tracing while reserving PII for fields like email or display name.

Applied to files:

  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
📚 Learning: 2026-06-11T19:39:10.493Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 13337
File: autogpt_platform/backend/backend/copilot/graphiti/reranker.py:0-0
Timestamp: 2026-06-11T19:39:10.493Z
Learning: In the Significant-Gravitas/AutoGPT Python backend, when calling the OpenAI Python client `chat.completions.create`, construct the `messages` payload using the concrete typed-dict variants from `openai.types.chat` (e.g., `ChatCompletionSystemMessageParam`, `ChatCompletionUserMessageParam`, etc.) rather than trying to instantiate `ChatCompletionMessageParam` directly. `ChatCompletionMessageParam` is a `Union` alias and is not constructible, so `ChatCompletionMessageParam(role=..., content=...)` should fail type checking. Build each message element with the appropriate concrete typed dict and then annotate the resulting list as `list[ChatCompletionMessageParam]` (e.g., `messages: list[ChatCompletionMessageParam] = [ChatCompletionSystemMessageParam(...), ...]`).

Applied to files:

  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
🔇 Additional comments (8)
autogpt_platform/backend/backend/api/features/chat/routes.py (1)

73-76: LGTM!

Also applies to: 1169-1190

autogpt_platform/backend/backend/copilot/session_tenancy.py (1)

1-85: LGTM!

autogpt_platform/backend/backend/api/features/chat/routes_test.py (2)

369-574: LGTM!


1001-1005: LGTM!

autogpt_platform/backend/backend/copilot/turn_queue.py (2)

52-55: LGTM!


299-306: LGTM!

Also applies to: 376-380

autogpt_platform/backend/backend/copilot/turn_queue_test.py (2)

44-62: LGTM!


374-454: LGTM!

Comment thread autogpt_platform/backend/backend/copilot/turn_queue.py Outdated
@ntindle

ntindle commented Jul 22, 2026

Copy link
Copy Markdown
Member Author

/batch orgs

@autogpt-batch-bot autogpt-batch-bot Bot added the batch:orgs batch-bot batch membership label Jul 22, 2026
@autogpt-batch-bot autogpt-batch-bot Bot mentioned this pull request Jul 22, 2026
24 tasks
@autogpt-batch-bot

Copy link
Copy Markdown

🤖 Added #13650 to batch orgs. Batch orgs (9): #13649, #13641, #13638, #13604, #13574, #13540, #13530, #13496, #13650.

Deploying the combined preview (#13651); /batch-merge lands them together.

… close parity gaps

Addresses the review round on #13650 (SECRT-2489).

- verify_session_org_membership called the global Prisma client, but
  dispatch_next_for_user runs inside the CoPilot executor where Prisma is not
  connected, so org-tagged queued turns raised into stream_registry's broad
  except and were never promoted. The two membership reads move into
  orgs.db.get_session_tenancy_membership, registered on DatabaseManager /
  DatabaseManagerAsyncClient, and are reached via db_accessors.orgs_db().
- Gate soft-deleted orgs (include Org, require deletedAt is None) and require
  Team.orgId == organization_id before honouring a team, matching
  get_request_context.
- Re-verify tenancy on POST /sessions/{id}/messages/pending too: pending
  messages drain straight into the running turn loop.
- Extract _resolve_promotable_head so a revoked queue head no longer wastes the
  freed promotion slot; the next valid queued session is promoted in the same
  tick.
- Rename verify_session_org_membership -> resolve_session_tenancy; collapse the
  triplicated rationale comment onto the module docstring.
- Tests: soft-deleted org, missing Org relation, team outside the session org,
  the pending-message gate, promote-next-after-revoked-head, the lost-CAS race,
  plus exact unique-lookup and helper-call-arg assertions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added cla: pending CLA not yet signed by all contributors platform/frontend AutoGPT Platform - Front end cla: signed CLA signed by all contributors and removed cla: signed CLA signed by all contributors labels Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

👋 Friendly reminder: This PR is waiting on a signed CLA.

All contributors need to sign our Contributor License Agreement before we can merge this PR.

➡️ Sign the CLA here

Why do we need a CLA?

The CLA protects both you and the project by clarifying the terms under which your contribution is made. It's a one-time process — once signed, it covers all your future contributions.

Common issues
  • Email mismatch: Make sure your Git commit email matches your GitHub account email
  • Stale branch: Sync your branch with the current dev branch and push the updated branch normally
  • Multiple authors: All commit authors need to sign, not just the PR author

If you have questions, just ask! 🙂

@github-actions github-actions Bot removed the cla: pending CLA not yet signed by all contributors label Aug 6, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@autogpt_platform/backend/backend/api/features/chat/routes.py`:
- Around line 1207-1220: Update queue_pending_for_http call handling in
autogpt_platform/backend/backend/api/features/chat/routes.py at lines 1207-1220
to preserve and pass the resolved turn_team_id for the /stream continuation
path. Also update lines 1547-1558 to retain the return value from
resolve_session_tenancy and propagate that resolved team context into
pending-message execution, ensuring both paths avoid stale team context.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8d254d33-3b2a-4b41-a278-63a3b5c13456

📥 Commits

Reviewing files that changed from the base of the PR and between 5afa8cf and 132df49.

📒 Files selected for processing (10)
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
  • autogpt_platform/backend/backend/api/features/orgs/db.py
  • autogpt_platform/backend/backend/api/features/orgs/model.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/data/db_manager.py
  • autogpt_platform/frontend/src/app/api/openapi.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
📜 Review details
⏰ Context from checks skipped due to timeout. (18)
  • GitHub Check: lint
  • GitHub Check: integration_test
  • GitHub Check: check API types
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Seer Code Review
  • GitHub Check: lint
  • GitHub Check: types
  • GitHub Check: end-to-end tests
  • GitHub Check: test (3.11)
  • GitHub Check: type-check (3.13)
  • GitHub Check: type-check (3.12)
  • GitHub Check: type-check (3.11)
  • GitHub Check: test (3.12)
  • GitHub Check: lint
  • GitHub Check: test (3.13)
  • GitHub Check: Check PR Status
  • GitHub Check: Analyze (typescript)
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (7)
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

autogpt_platform/backend/**/*.py: Use poetry run ... command for executing Python package dependencies
Use top-level imports only — avoid local/inner imports except for lazy imports of heavy optional dependencies like openpyxl
Use absolute imports with from backend.module import ... for cross-package imports; single-dot relative imports are acceptable for sibling modules within the same package; avoid double-dot relative imports
Do not use duck typing — avoid hasattr/getattr/isinstance for type dispatch; use typed interfaces/unions/protocols instead
Use Pydantic models over dataclass/namedtuple/dict for structured data
Do not use linter suppressors — no # type: ignore, # noqa, # pyright: ignore; fix the type/code instead
Prefer list comprehensions over manual loop-and-append patterns
Use early return with guard clauses first to avoid deep nesting
Use %s for deferred interpolation in debug log statements for efficiency; use f-strings elsewhere for readability (e.g., logger.debug("Processing %s items", count) vs logger.info(f"Processing {count} items"))
Sanitize error paths by using os.path.basename() in error messages to avoid leaking directory structure
Be aware of TOCTOU (Time-Of-Check-Time-Of-Use) issues — avoid check-then-act patterns for file access and credit charging
Use transaction=True for Redis pipelines to ensure atomicity on multi-step operations
Use max(0, value) guards for computed values that should never be negative
Keep files under ~300 lines; if a file grows beyond this, split by responsibility (extract helpers, models, or a sub-module into a new file)
Keep functions under ~40 lines; extract named helpers when a function grows longer
...

Files:

  • autogpt_platform/backend/backend/api/features/orgs/model.py
  • autogpt_platform/backend/backend/data/db_manager.py
  • autogpt_platform/backend/backend/api/features/orgs/db.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
autogpt_platform/backend/backend/api/features/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Update routes in '/backend/backend/api/features/' and add/update Pydantic models in the same directory for API development

Files:

  • autogpt_platform/backend/backend/api/features/orgs/model.py
  • autogpt_platform/backend/backend/api/features/orgs/db.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
autogpt_platform/{backend,autogpt_libs}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/api/features/orgs/model.py
  • autogpt_platform/backend/backend/data/db_manager.py
  • autogpt_platform/backend/backend/api/features/orgs/db.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
autogpt_platform/backend/**/api/**/*.py

📄 CodeRabbit inference engine (autogpt_platform/backend/AGENTS.md)

autogpt_platform/backend/**/api/**/*.py: Use Security() instead of Depends() for authentication dependencies to get proper OpenAPI security specification
Follow SSE (Server-Sent Events) protocol: use data: lines for frontend-parsed events (must match Zod schema) and : comment lines for heartbeats/status

Files:

  • autogpt_platform/backend/backend/api/features/orgs/model.py
  • autogpt_platform/backend/backend/api/features/orgs/db.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
autogpt_platform/backend/backend/data/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

All data access in backend requires user ID checks; verify this for any 'data/*.py' changes

Files:

  • autogpt_platform/backend/backend/data/db_manager.py
autogpt_platform/**/data/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

For changes touching data/*.py, validate user ID checks or explain why not needed

Files:

  • autogpt_platform/backend/backend/data/db_manager.py
autogpt_platform/backend/**/*_test.py

📄 CodeRabbit inference engine (autogpt_platform/backend/AGENTS.md)

autogpt_platform/backend/**/*_test.py: Use pytest with snapshot testing for API responses
Colocate test files with source files using *_test.py naming convention
Mock at boundaries — mock where the symbol is used, not where it's defined; after refactoring, update mock targets to match new module paths
Use AsyncMock from unittest.mock for async functions in tests
When writing tests, use Test-Driven Development (TDD): write failing tests marked with @pytest.mark.xfail before implementation, then remove the marker once the implementation is complete
When creating snapshots in tests, use poetry run pytest path/to/test.py --snapshot-update; always review snapshot changes with git diff before committing

Files:

  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
🧠 Learnings (19)
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/backend/api/features/orgs/model.py
  • autogpt_platform/backend/backend/data/db_manager.py
  • autogpt_platform/backend/backend/api/features/orgs/db.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
📚 Learning: 2026-03-05T15:42:08.207Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.

Applied to files:

  • autogpt_platform/backend/backend/api/features/orgs/model.py
  • autogpt_platform/backend/backend/data/db_manager.py
  • autogpt_platform/backend/backend/api/features/orgs/db.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
📚 Learning: 2026-03-16T16:35:40.236Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12440
File: autogpt_platform/backend/backend/api/features/workflow_import.py:54-63
Timestamp: 2026-03-16T16:35:40.236Z
Learning: Avoid using the word 'competitor' in public-facing identifiers and text. Use neutral naming for API paths, model names, function names, and UI text. Examples: rename 'CompetitorFormat' to 'SourcePlatform', 'convert_competitor_workflow' to 'convert_workflow', '/competitor-workflow' to '/workflow'. Apply this guideline to files under autogpt_platform/backend and autogpt_platform/frontend.

Applied to files:

  • autogpt_platform/backend/backend/api/features/orgs/model.py
  • autogpt_platform/backend/backend/data/db_manager.py
  • autogpt_platform/backend/backend/api/features/orgs/db.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
📚 Learning: 2026-03-31T15:37:38.626Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12623
File: autogpt_platform/backend/backend/copilot/tools/agent_generator/fixer.py:37-47
Timestamp: 2026-03-31T15:37:38.626Z
Learning: When validating/constructing Anthropic API model IDs in Significant-Gravitas/AutoGPT, allow the hyphen-separated Claude Opus 4.6 model ID `claude-opus-4-6` (it corresponds to `LlmModel.CLAUDE_4_6_OPUS` in `autogpt_platform/backend/backend/blocks/llm.py`). Do NOT require the dot-separated form in Anthropic contexts. Only OpenRouter routing variants should use the dot separator (e.g., `anthropic/claude-opus-4.6`); `claude-opus-4-6` should be treated as correct when passed to Anthropic, and flagged only if it’s used in the OpenRouter path where the dot form is expected.

Applied to files:

  • autogpt_platform/backend/backend/api/features/orgs/model.py
  • autogpt_platform/backend/backend/data/db_manager.py
  • autogpt_platform/backend/backend/api/features/orgs/db.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
📚 Learning: 2026-04-15T02:43:36.890Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12780
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:0-0
Timestamp: 2026-04-15T02:43:36.890Z
Learning: When reviewing Python exception handlers, do not flag `isinstance(e, X)` checks as dead/unreachable if the caught exception `X` is a subclass of the exception type being handled. For example, if `X` (e.g., `VirusScanError`) inherits from `ValueError` (directly or via an intermediate class) and it can be raised within an `except ValueError:` block, then `isinstance(e, X)` inside that handler is reachable and should not be treated as dead code.

Applied to files:

  • autogpt_platform/backend/backend/api/features/orgs/model.py
  • autogpt_platform/backend/backend/data/db_manager.py
  • autogpt_platform/backend/backend/api/features/orgs/db.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
📚 Learning: 2026-05-23T05:29:43.085Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 13200
File: autogpt_platform/backend/backend/executor/scheduler.py:590-593
Timestamp: 2026-05-23T05:29:43.085Z
Learning: When reviewing Python code that uses Pydantic discriminated/tagged unions (e.g., `Annotated[Union[...], Field(discriminator="kind")]`), recognize that using `isinstance(x, SomeVariantInfo)` to narrow the union is an intentional and correct runtime guard and should also enable static type narrowing in tools like Pyright. Do not recommend replacing such `isinstance`-based narrowing with `cast(...)` when the check already proves the variant at runtime.

Applied to files:

  • autogpt_platform/backend/backend/api/features/orgs/model.py
  • autogpt_platform/backend/backend/data/db_manager.py
  • autogpt_platform/backend/backend/api/features/orgs/db.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
📚 Learning: 2026-04-22T11:46:04.431Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/config.py:0-0
Timestamp: 2026-04-22T11:46:04.431Z
Learning: Do not flag the Claude Sonnet 4.6 model ID as incorrect when it uses the project’s established hyphenated convention: `anthropic/claude-sonnet-4-6`. This hyphen form is the intentional, production convention and should be treated as valid (including in files like llm.py, blocks tests, reasoning.py, `_is_anthropic_model` tests, and config defaults). Note that OpenRouter also accepts the dot variant `anthropic/claude-sonnet-4.6`, so either form may be tolerated, but `anthropic/claude-sonnet-4-6` should be considered the standard to match project usage.

Applied to files:

  • autogpt_platform/backend/backend/api/features/orgs/model.py
  • autogpt_platform/backend/backend/data/db_manager.py
  • autogpt_platform/backend/backend/api/features/orgs/db.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
📚 Learning: 2026-04-22T11:46:12.892Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/baseline/service.py:322-332
Timestamp: 2026-04-22T11:46:12.892Z
Learning: In this codebase (Significant-Gravitas/AutoGPT), OpenRouter-routed Anthropic model IDs should use the hyphen-separated convention (e.g., `anthropic/claude-sonnet-4-6`, `anthropic/claude-opus-4-6`). Although OpenRouter may accept both hyphen and dot variants, treat the hyphen-separated form as the intended, correct codebase-wide convention and do not flag it as an error. Only flag the dot-separated variant (e.g., `anthropic/claude-sonnet-4.6`) as incorrect when reviewing/validating model ID strings for OpenRouter-routed Anthropic models.

Applied to files:

  • autogpt_platform/backend/backend/api/features/orgs/model.py
  • autogpt_platform/backend/backend/data/db_manager.py
  • autogpt_platform/backend/backend/api/features/orgs/db.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
📚 Learning: 2026-05-07T18:48:14.242Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 13040
File: autogpt_platform/backend/backend/blocks/llm.py:0-0
Timestamp: 2026-05-07T18:48:14.242Z
Learning: In this repository, isort may split imports from the same module into separate blocks when some imports are aliased (e.g., `from module import X as Y`) and others are not. Preserve the two-block layout when it results from isort (such as keeping `from openai.types.chat import ChatCompletion as OpenAIChatCompletion` separate from non-aliased imports from `openai.types.chat`). Do not treat that split as a style issue during review; merging them into a single block can fail CI with `Imports are incorrectly sorted and/or formatted`.

Applied to files:

  • autogpt_platform/backend/backend/api/features/orgs/model.py
  • autogpt_platform/backend/backend/data/db_manager.py
  • autogpt_platform/backend/backend/api/features/orgs/db.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
📚 Learning: 2026-05-26T14:24:34.866Z
Learnt from: Abhi1992002
Repo: Significant-Gravitas/AutoGPT PR: 13217
File: autogpt_platform/backend/backend/api/features/search/service.py:137-137
Timestamp: 2026-05-26T14:24:34.866Z
Learning: In the Significant-Gravitas/AutoGPT backend, treat `user_id` (an opaque UUID used only for correlation/tracing) as non-PII. Do not flag direct logging of `user_id` in `logger.warning`/`logger.info` statements as a PII exposure issue, as the established convention is to log `user_id` for tracing while reserving PII for fields like email or display name.

Applied to files:

  • autogpt_platform/backend/backend/api/features/orgs/model.py
  • autogpt_platform/backend/backend/data/db_manager.py
  • autogpt_platform/backend/backend/api/features/orgs/db.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
📚 Learning: 2026-06-11T19:39:10.493Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 13337
File: autogpt_platform/backend/backend/copilot/graphiti/reranker.py:0-0
Timestamp: 2026-06-11T19:39:10.493Z
Learning: In the Significant-Gravitas/AutoGPT Python backend, when calling the OpenAI Python client `chat.completions.create`, construct the `messages` payload using the concrete typed-dict variants from `openai.types.chat` (e.g., `ChatCompletionSystemMessageParam`, `ChatCompletionUserMessageParam`, etc.) rather than trying to instantiate `ChatCompletionMessageParam` directly. `ChatCompletionMessageParam` is a `Union` alias and is not constructible, so `ChatCompletionMessageParam(role=..., content=...)` should fail type checking. Build each message element with the appropriate concrete typed dict and then annotate the resulting list as `list[ChatCompletionMessageParam]` (e.g., `messages: list[ChatCompletionMessageParam] = [ChatCompletionSystemMessageParam(...), ...]`).

Applied to files:

  • autogpt_platform/backend/backend/api/features/orgs/model.py
  • autogpt_platform/backend/backend/data/db_manager.py
  • autogpt_platform/backend/backend/api/features/orgs/db.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
📚 Learning: 2026-03-01T07:58:56.207Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:10030-10037
Timestamp: 2026-03-01T07:58:56.207Z
Learning: When a backend field represents sensitive data, use a secret type (e.g., Pydantic SecretStr with length constraints) so OpenAPI marks it as a password/writeOnly field. Apply this pattern to similar sensitive request fields across API schemas so generated TypeScript clients and docs treat them as secrets and do not mishandle sensitivity. Review all openapi.jsons where sensitive inputs are defined and replace plain strings with SecretStr-like semantics with appropriate minLength constraints.

Applied to files:

  • autogpt_platform/frontend/src/app/api/openapi.json
📚 Learning: 2026-04-14T06:39:49.111Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12773
File: autogpt_platform/frontend/src/app/api/openapi.json:12803-12806
Timestamp: 2026-04-14T06:39:49.111Z
Learning: In OpenAPI specs, ensure the schema/message length caps for the StreamChatRequest.message and QueuePendingMessageRequest.message fields are set to the intended values: StreamChatRequest.message maxLength must be 64000 and QueuePendingMessageRequest.message maxLength must be 32000. Keep QueuePendingMessageRequest.message consistent with PendingMessage.content, and ensure the pending (queue) ceiling never exceeds the stream ceiling because both ultimately feed the same LLM context window. Update any legacy smaller limits (e.g., 4000/16000) to these newer ceilings.

Applied to files:

  • autogpt_platform/frontend/src/app/api/openapi.json
📚 Learning: 2026-03-07T07:43:09.871Z
Learnt from: kcze
Repo: Significant-Gravitas/AutoGPT PR: 12328
File: autogpt_platform/frontend/src/app/api/openapi.json:1116-1118
Timestamp: 2026-03-07T07:43:09.871Z
Learning: For autogpt_platform/frontend/src/app/api/openapi.json, preserve the existing behavior: HTTPBearerJWT is declared at the router level with Depends(auth.get_user_id) returning None for unauthenticated users; treat as optional auth. Do not change per-operation security descriptions unless you plan a repo-wide OpenAPI update. If you change this file, prefer clarifying operation descriptions rather than altering security requirements.

Applied to files:

  • autogpt_platform/frontend/src/app/api/openapi.json
📚 Learning: 2026-04-21T04:35:34.710Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12865
File: autogpt_platform/backend/backend/data/credit.py:1584-1584
Timestamp: 2026-04-21T04:35:34.710Z
Learning: When reviewing this codebase, don’t flag snake_case attribute names (e.g., `subscription_tier`, `stripe_customer_id`, `top_up_config`) on the app-layer Pydantic `User` model as “wrong” field names. These are correct for the app-layer model and are expected to be mapped from the Prisma-layer camelCase fields (e.g., `subscriptionTier`, `stripeCustomerId`) inside methods like `User.from_db()`. Only Prisma-returned/raw objects would use camelCase, but functions like `get_user_by_id(user_id: str)` are expected to return the Pydantic app-layer model.

Applied to files:

  • autogpt_platform/backend/backend/data/db_manager.py
📚 Learning: 2026-05-07T15:32:39.703Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 13033
File: autogpt_platform/backend/backend/data/generate_data.py:111-117
Timestamp: 2026-05-07T15:32:39.703Z
Learning: When reviewing the Python data-generation layer, do not treat missing `user_id`/user filtering in calls to graph-metadata resolvers as a security issue if the `graph_id` inputs are already guaranteed to be user-scoped by earlier upstream SQL (e.g., `WHERE "userId" = ...`). In particular, `_resolve_agent_name(graph_id)` in `generate_data.py` correctly calls `get_graph_metadata(graph_id=graph_id)` without a `user_id` parameter by design, because name resolution must also work for user-executed shared/marketplace agents that the user may not own.

Applied to files:

  • autogpt_platform/backend/backend/data/db_manager.py
📚 Learning: 2026-03-04T08:04:35.881Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12273
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:216-220
Timestamp: 2026-03-04T08:04:35.881Z
Learning: In the AutoGPT Copilot backend, ensure that SVG images are not treated as vision image types by excluding 'image/svg+xml' from INLINEABLE_MIME_TYPES and MULTIMODAL_TYPES in tool_adapter.py; the Claude API supports PNG, JPEG, GIF, and WebP for vision. SVGs (XML text) should be handled via the text path instead, not the vision path.

Applied to files:

  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
📚 Learning: 2026-04-01T04:17:41.600Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12632
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:0-0
Timestamp: 2026-04-01T04:17:41.600Z
Learning: When reviewing AutoGPT Copilot tool implementations, accept that `readOnlyHint=True` (provided via `ToolAnnotations`) may be applied unconditionally to *all* tools—even tools that have side effects (e.g., `bash_exec`, `write_workspace_file`, or other write/save operations). Do **not** flag these tools for having `readOnlyHint=True`; this is intentional to enable fully-parallel dispatch by the Anthropic SDK/CLI and has been E2E validated. Only flag `readOnlyHint` issues if they conflict with the established `ToolAnnotations` behavior (e.g., missing/incorrect propagation relative to the intended annotation mechanism).

Applied to files:

  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
📚 Learning: 2026-06-06T12:22:37.648Z
Learnt from: anvyle
Repo: Significant-Gravitas/AutoGPT PR: 13302
File: autogpt_platform/backend/backend/copilot/tools/agent_search.py:579-583
Timestamp: 2026-06-06T12:22:37.648Z
Learning: When writing LLM-facing instruction strings that trigger tool calls in this AutoGPT codebase, use the exact registered tool name `view_agent_output` (as defined in `backend/copilot/tools/agent_output.py` via its `name` property and exported via `TOOL_REGISTRY`). Do not reference the bare name `agent_output`, since it is not a valid tool name and will cause tool invocation to fail.

Applied to files:

  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
🔇 Additional comments (12)
autogpt_platform/backend/backend/api/features/orgs/model.py (1)

35-43: LGTM!

autogpt_platform/backend/backend/api/features/orgs/db.py (1)

7-7: LGTM!

Also applies to: 18-24, 183-230

autogpt_platform/backend/backend/data/db_manager.py (1)

34-37: LGTM!

Also applies to: 447-450, 742-742

autogpt_platform/backend/backend/copilot/session_tenancy.py (1)

3-35: LGTM!

Also applies to: 54-90

autogpt_platform/backend/backend/copilot/session_tenancy_test.py (1)

3-199: LGTM!

autogpt_platform/backend/backend/api/features/chat/routes.py (1)

76-76: LGTM!

Also applies to: 130-132, 1144-1144, 1524-1524

autogpt_platform/frontend/src/app/api/openapi.json (1)

3704-3706: LGTM!

Also applies to: 3992-3994

autogpt_platform/backend/backend/copilot/turn_queue.py (3)

42-42: LGTM!

Also applies to: 55-55


295-302: LGTM!

Also applies to: 353-357


393-435: LGTM!

autogpt_platform/backend/backend/copilot/turn_queue_test.py (2)

10-18: LGTM!

Also applies to: 201-220


410-432: LGTM!

Also applies to: 435-477, 480-506, 518-535

Comment thread autogpt_platform/backend/backend/api/features/chat/routes.py
@ntindle

ntindle commented Aug 6, 2026

Copy link
Copy Markdown
Member Author

🤖 Addressed the review round in 132df49fb. Summary of what changed and what I pushed back on — inline replies are on each thread.

Blockers

1. Prisma called from the Prisma-less executor (@kcze, Codex, Cursor Bugbot, reviewer) — real, fixed. The two membership reads moved into get_session_tenancy_membership (backend/api/features/orgs/db.py) and are registered on DatabaseManager / DatabaseManagerAsyncClient (backend/data/db_manager.py, the three get_user_default_team sites). session_tenancy.py now reaches them through db_accessors.orgs_db(), the same in-process-or-RPC indirection every other DB call on the dispatch_next_for_user path uses. It returns a plain SessionTenancyMembership(org_active, team_active) pydantic model rather than Prisma rows so the RPC response serializer accepts it; the revocation decision (and the exception, which can't cross the RPC boundary) stays in session_tenancy.py.

2. Soft-deleted org not blocked — real, fixed. The org gate now uses include={"Org": True} and requires Org is not None and Org.deletedAt is None on top of status == ACTIVE, matching get_request_context's "This organization has been deleted" 403. A dangling member row with no Org relation fails closed too.

Should-Fix

  • POST /sessions/{id}/messages/pending ungated — fixed. queue_pending_message re-verifies before accepting the follow-up and raises the same 403 (shared SESSION_ORG_REVOKED_DETAIL, 403 added to both endpoints' responses).
  • Team.orgId not checked — fixed. include={"Team": True} plus Team.orgId == organization_id, otherwise stripped to org-home.
  • Revoked head wastes the promotion slot — fixed. Extracted _resolve_promotable_head(user_id, queued), which walks the queue in FIFO order, drops each revoked session (queued → idle + cache invalidate) and returns the first still-valid one with its resolved team. A bounded single pass rather than re-entering dispatch_next_for_user, so there's no recursion depth to guard and no second queue read. The overstated inline comment is gone.
  • Weak test assertions — fixed. Both turn_queue tests now assert_awaited_once_with(...) the session's own tenancy; routes_test and session_tenancy_test assert the exact orgId_userId / teamId_userId where-clauses and the includes.

Nits

Renamed verify_session_org_membershipresolve_session_tenancy (it resolves, it doesn't just gate). Collapsed the triplicated rationale comment: the module docstring is now marked as the single source of truth and each call site carries a one-line pointer plus only site-specific context. Hoisted the inline import types / from prisma.enums import OrgMemberStatus in the tests.

Pushed back on

Nice-to-Have 1 — skip re-verification when the session tenancy already matches ctx (routes.py:1211). Not doing it. The saving is at most two point lookups on unique indexes — sub-millisecond — on a request that then spends seconds in an LLM round trip plus tool calls. Against that, the guard makes a revocation gate conditional on two tenancy triples being equal, which delegates the check to get_request_context. Nice-to-Have 2 on the same review correctly notes those are two hand-maintained copies of the policy; today they agree (this PR made them agree), but the moment they diverge the fast path stops being an optimisation and becomes a bypass on the dominant code path — exactly the bug class SECRT-2489 exists to close. Full argument on the thread.

Nice-to-Have 2 — factor the shared membership primitives out of get_request_context. Partly done, deliberately not finished here. get_session_tenancy_membership is now the single implementation of the membership predicate (ACTIVE + deletedAt + Team.orgId), and this PR made the two agree. Collapsing get_request_context onto it as well needs the five role flags (isOwner/isAdmin/isBillingManager on the org, isAdmin/isBillingManager on the team) to come back over the same call, which widens the return type and reshapes the auth dependency — a refactor with its own blast radius that doesn't belong in a security fix. Worth a follow-up.

Tests

New coverage: soft-deleted org, missing Org relation, team-outside-session-org (helper + route), the pending-message gate (403 + happy path), promotion of the next queued session after a revoked head, and the lost-CAS race during the drop.

@ntindle

ntindle commented Aug 6, 2026

Copy link
Copy Markdown
Member Author

/review

@autogpt-pr-reviewer

Copy link
Copy Markdown

Queued a review for PR #13650 at 132df49.

@autogpt-pr-reviewer autogpt-pr-reviewer Bot 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.

📋 Automated Review — PR #13650

PR #13650 — fix(backend): re-verify org/team membership on every chat turn
Author: ntindle | Files: 10

🎯 Verdict: APPROVE

PR Description Quality

✅ Has Why (closes SECRT-2489 privilege-persistence gap) + What (per-turn tenancy re-verification) + How (gate at /stream, /messages/pending, queue promotion; hard-403 stale org, soft-strip stale team, skip untagged sessions). The description also honestly enumerates the dispatch paths it does not cover (scheduled-followup, engine-switch, sub-session waiter), tracked separately.

What This PR Does

Previously a chat session persisted its organization_id/team_id and trusted that tenancy on every turn, while membership was only checked once at session creation — so a user removed or suspended from an org could keep running agents and spending credits under that org indefinitely. This PR re-verifies org/team membership on every turn dispatch: a revoked org membership hard-403s, a stale team is silently stripped back to the org home, personal/untagged sessions are left untouched, and the queued-then-revoked promotion race is closed.

Specialist Findings

🛡️ Security ✅ — Traced every branch of the new gate; it fails closed everywhere that matters (revoked → SessionOrgMembershipRevoked → 403), tenancy comes from the server-persisted session row (not client-controllable), and both lookups are parameterized find_unique calls. The two prior blockers (soft-deleted-org bypass, executor Prisma access) are confirmed fixed. Only residual risk is the acknowledged uncovered dispatch paths, tracked on SECRT-2489.

🏗️ Architecture ✅ — Dependency direction is correct (call sites → session_tenancyorgs_db() RPC accessor, avoiding the global Prisma client in the executor). 🟠 Notes the membership policy is now duplicated between get_session_tenancy_membership (orgs/db.py:183) and get_request_context, kept in sync by docstring/tests only, and the gate lives at N entry points rather than the execution choke point.

Performance ✅ — Adds ≤2 indexed, short-circuiting unique-key reads per turn (O(1)); dwarfed by the LLM round-trip that follows. On the executor path each check is a sequential RPC round-trip, bounded by queue depth. A short-TTL membership cache is the suggested scaling lever, not a blocker.

🧪 Testing ✅ — ~90% coverage, TDD-first; the core policy chain runs for real with only the two Prisma reads stubbed, so route/wiring regressions are actually caught. Negative cases (revoked, suspended, soft-deleted, missing Org relation, stale team, cross-org team) all present, plus the CAS-race drop. 🟠 Minor gaps: no untagged-skip test on the pending endpoint, no transient-DB-failure test.

📖 Quality ✅ — Naming is precise, and the "policy lives in the module docstring" single-source pattern is exemplary. 🟠 The 403 gate block is copy-pasted across stream_chat_post and queue_pending_message; worth extracting to a shared helper.

📦 Product ⚠️ — Happy path and team-strip are invisible-and-correct. 🟠 But the new 403 has no [code:...] marker, so the frontend stream handler falls through to a generic banner likely offering a "Try Again" that can only re-403; and a queued turn dropped on revocation fails silently with only a server log. Both are follow-up UX gaps, not defects in the security fix.

📬 Discussion ✅ — All 30 review threads resolved, each with an author response; the two author pushbacks are reasoned and defensible. GitHub CI reported 43/43 green. Note: a stale CHANGES_REQUESTED bot review predating the fix commit (132df49fb) still governs reviewDecision and needs dismissal/re-review to unblock merge.

🔎 QA ✅ — Exercised live against a running stack across 11 scenarios: happy path (200), revoked org via a distinct second org isolating the new code path (403 with exact new detail), pending-message choke point (403), soft-deleted org (403), stale-team strip (200), untagged legacy session (200), no-auth (401), and session-stays-idle-after-403. Independently re-ran the suite: 49 associated tests pass. Queue-promotion path covered by unit tests only (can't be raced live).

🟠 Should Fix

  1. Duplicated 403 tenancy gate across two handlers (routes.py:1207 & ~1546) — extract _enforce_session_tenancy(session, user_id) -> str | None so both choke points (and future ones) stay in sync. (Flagged by: architect, quality — 2 specialists)
  2. New 403 has no curated frontend handling (routes.py:130) — plain-string detail falls through copilotStreamErrorHandlers.ts to a generic banner that likely offers a non-working "Try Again"; emit a [code:org_membership_revoked] marker or add an explicit non-retryable case with recovery copy. (Flagged by: product)
  3. Queued-turn revocation drop is silent (turn_queue.py:~421) — a dropped queued→idle turn gives the user no signal; persist a retryable/terminal marker so the UI can explain it. (Flagged by: product)
  4. Policy fork with get_request_context (orgs/db.py:183) — membership predicate is re-implemented and guarded only by prose/tests; extract a shared predicate so a future org-status field can't silently drift the per-turn gate. (Flagged by: architect, security — 2 specialists)
  5. Missing pending-path untagged-skip and transient-failure tests (routes.py:1537, session_tenancy.py:71) — add an organization_id=None skip test mirroring the stream path, and one test pinning the intended behavior when the membership read raises a non-revocation error. (Flagged by: testing)

🟡 Nice to Have

  1. Short-TTL membership cache (session_tenancy.py) — a 5–15s Redis cache keyed on (user_id, org_id, team_id) across the three choke points would eliminate steady-state read load as chat volume scales, keeping the revocation window tight. (performance)
  2. Skip-next on transient promotion error (turn_queue.py:~410) — a non-revocation error currently aborts the whole dispatch tick; skip that head and continue so one membership-read hiccup doesn't stall the user's queue. (security, performance)

🔵 Nits

  1. turn_team_id computed then overwritten (routes.py:1204) — the org-branch of the first assignment is dead when organization_id is set; a one-line comment would clarify. (quality)
  2. Shared test-stub helpers (session_tenancy_test.py:28, routes_test.py:~382) — near-duplicate _org_member/_team_member fixtures could live in a shared conftest. (quality)

QA Screenshots

Screenshot Description
copilot ready Copilot page loads authenticated, full stack up ✅
copilot response Message sent through UI, reply renders end-to-end ✅

Human Review Needed

YES — This change modifies an authorization/trust boundary (per-turn tenancy enforcement between users and orgs/teams). A maintainer re-review is warranted to clear the stale CHANGES_REQUESTED decision, even though the code is sound and QA-proven.

Risk Assessment

Merge risk: LOW | Rollback: EASY (isolated, additive gate; revertable without schema/data migration)

CI Status

GitHub CI: reported 43/43 required checks green on the fix commit (132df49fb) per the discussion review; no merge conflicts. Local harness: backend lint ✅, frontend lint/typecheck/build ✅; the frontend pnpm test:unit suite failed in the sandbox — this is an unrelated frontend suite for a backend-only change and is environment skew against the green GitHub run, not a blocker. QA independently re-ran the 49 backend tests associated with this PR: all pass.


UI Testing — Variant Results

✅ local: Per-turn org/team re-verification works correctly at every dispatch choke point — revoked/suspended/soft-deleted org returns 403, stale team soft-strips and proceeds, legacy sessions unaffected, all 49 associated tests pass on independent run.

✅ hosted: Per-turn org/team re-verification works live: non-member org yields 403 at both /stream and /messages/pending with no dispatch, active member and restored membership return 200, negative auth returns 401, and both new unit suites pass (25 tests).

sessions keep their persisted user message; a re-send from a valid
context re-dispatches it.
"""
for session in queued:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 🟢 low (security/availability / error handling)

The promotion loop only catches SessionOrgMembershipRevoked. A transient error from resolve_session_tenancy (RPC timeout, Prisma error) on one queued head propagates out of dispatch_next_for_user and aborts the whole tick, so a still-valid queued session behind it is not promoted. Fails closed (no wrong-tenant run) but couples one membership-read hiccup to the user's entire queue availability.

Suggestion: Catch non-revoked exceptions per-session and continue to the next queued head (logging distinctly), rather than letting them abort the whole dispatch tick.

*team_id* is ``None`` or the org already failed.

Returns plain booleans rather than Prisma rows so Prisma-less processes
(the CoPilot executor) can call this over the DatabaseManager RPC.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 🟢 low (security/auth-parity verification)

get_session_tenancy_membership gates the org on ACTIVE OrgMember + Org.deletedAt is None and claims exact parity with get_request_context. If get_request_context also inspects any org-level suspension/status column beyond deletedAt, this per-turn re-check would be weaker than the header path.

Suggestion: Confirm get_request_context checks no additional Organization-level status field; if it does, mirror that predicate here so the re-check cannot pass where a normal request would 403.

return org_id, ws_id


async def get_session_tenancy_membership(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 🟡 medium (architect/policy-duplication)

get_session_tenancy_membership re-implements the ACTIVE-member + Org.deletedAt + Team.orgId membership policy that get_request_context enforces. The equivalence is guaranteed only by docstring and tests, so a future change to get_request_context (new OrgMemberStatus, billing suspension, invite-pending) silently drifts this copy and the per-turn gate diverges from session-creation gating.

Suggestion: Extract the shared membership predicate (e.g. membership_is_active(org_member) / team_is_valid(team_member, org_id)) into one function consumed by both get_request_context and get_session_tenancy_membership so the policy has a single definition.

@@ -1195,6 +1204,21 @@ async def stream_chat_post(
turn_org_id = session.organization_id or ctx.org_id
turn_team_id = session.team_id if session.organization_id else ctx.team_id

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 🟢 low (architect/duplication)

The tenancy gate (if session.organization_id is not None -> try resolve_session_tenancy -> except SessionOrgMembershipRevoked -> raise HTTPException 403) is copy-pasted between stream_chat_post and queue_pending_message (~line 1543). Each new HTTP dispatch entry point will duplicate it again.

Suggestion: Factor the gate into a shared helper or FastAPI dependency (e.g. _enforce_session_tenancy(session, user_id) returning the resolved team_id) and call it from both handlers.

@@ -0,0 +1,90 @@
"""Per-turn re-verification of a chat session's persisted org/team.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 🟡 medium (architect/enforcement-placement)

Tenancy is re-verified at each dispatch entry point (/stream, /messages/pending, queue promotion) rather than at the single execution point where organization_id/team_id are consumed for billing and tool execution. The PR's own listed-not-covered paths (scheduled-followup dispatcher, engine-switch continuation, sub-session waiter) demonstrate that the invariant is only as strong as each author remembering to add the gate.

Suggestion: Track a follow-up to enforce (or duplicate-guard) tenancy at the turn-execution choke point (dispatch_turn/turn loop) so bypass is structurally impossible and entry-point checks become fast-fail optimizations.

@@ -1195,6 +1204,21 @@ async def stream_chat_post(
turn_org_id = session.organization_id or ctx.org_id

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 🟢 low (quality/readability / structure)

turn_team_id is computed from session/ctx, then immediately overwritten by resolve_session_tenancy whenever organization_id is not None, making the org-branch of the first assignment dead in that path. Momentarily confusing to a maintainer.

Suggestion: Add a brief inline comment clarifying the first assignment only applies to untagged sessions, or restructure so the org-tagged branch computes turn_team_id in a single place.

def _patch_membership_reads(mocker, *, org_member, team_member):
prisma = MagicMock()
prisma.orgmember.find_unique = AsyncMock(return_value=org_member)
prisma.teammember.find_unique = AsyncMock(return_value=team_member)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 🟢 low (quality/test duplication)

_patch_membership_reads, _org_member, and _team_member helpers are near-duplicates of the same helpers in routes_test.py (~line 382), risking drift if the Prisma row shape changes.

Suggestion: Consider sharing these row-stub/patch helpers via a conftest fixture or a small shared test-support module.


config = ChatConfig()

# 403 detail shared by every turn-dispatch choke point that re-verifies a

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 🟡 medium (product/error-ux)

The new 403 detail is a plain string with no [code:...] prefix, so the frontend stream handler (copilotStreamErrorHandlers.ts) falls through to generic/inline handling. Removed-from-org is terminal (retry always re-403s) but the UI gives no distinct, non-retryable treatment and likely still offers a 'Try Again' affordance that loops forever.

Suggestion: Emit this 403 with a [code:org_membership_revoked] prefix (or add an explicit case in handleStreamError) so the UI can show clear, non-retryable copy with recovery guidance (e.g. switch to personal workspace / contact org admin).

organization_id=session.organization_id,
team_id=session.team_id,
)
except SessionOrgMembershipRevoked:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 🟡 medium (product/silent-failure)

_resolve_promotable_head drops a revoked queued session queued→idle with only a server-side logger.warning. The user who queued a follow-up gets no error, notification, or completion — the message silently stops progressing, and they have no way to know a re-send is required.

Suggestion: Persist a retryable/terminal-error marker or notification on the dropped session so the chat UI can explain the membership revocation, consistent with how other terminal turn failures surface to the user.


config = ChatConfig()

# 403 detail shared by every turn-dispatch choke point that re-verifies a

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 🟢 low (product/error-copy)

The 403 message states the user was removed from the org but offers no next step, leaving a mid-conversation user with no clear path forward.

Suggestion: Add recovery guidance to the detail string (e.g. suggest starting a new session in their personal workspace or contacting an org admin).

@autogpt-pr-reviewer
autogpt-pr-reviewer Bot dismissed their stale review August 6, 2026 13:35

Superseded by a newer automated review.

@github-actions github-actions Bot added the conflicts Automatically applied to PRs with merge conflicts label Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

This pull request has conflicts with the base branch, please resolve those so we can evaluate the pull request.

# Conflicts:
#	autogpt_platform/backend/backend/api/features/chat/routes.py
#	autogpt_platform/backend/backend/api/features/chat/routes_test.py
#	autogpt_platform/backend/backend/api/features/orgs/db.py
#	autogpt_platform/backend/backend/data/db_manager.py
@github-actions github-actions Bot added cla: pending CLA not yet signed by all contributors platform/blocks and removed cla: pending CLA not yet signed by all contributors cla: signed CLA signed by all contributors labels Aug 12, 2026
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@github-actions

Copy link
Copy Markdown
Contributor

Conflicts have been resolved! 🎉 A maintainer will review the pull request shortly.

@github-actions github-actions Bot removed the conflicts Automatically applied to PRs with merge conflicts label Aug 12, 2026
@ntindle ntindle changed the title fix(backend): re-verify org/team membership on every chat turn fix(backend): harden Copilot session tenancy Aug 12, 2026
Comment on lines +199 to +200
if persisted_session.organization_id is None:
raise SessionAdmissionError("session_tenancy_unverifiable")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: The tenancy check for in-flight turns incorrectly rejects requests for legacy sessions even when a valid organization_id is provided by the caller.
Severity: MEDIUM

Suggested Fix

Update the conditional check for in-flight turns to be consistent with the non-in-flight path. The check should only raise an error if both persisted_session.organization_id and the caller-provided organization_id are None.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: autogpt_platform/backend/backend/copilot/sdk/session_waiter.py#L199-L200

Potential issue: In `run_copilot_turn_via_queue`, the logic for handling in-flight turns
has an inconsistent tenancy check for legacy sessions. When a turn is in-flight for a
session where `persisted_session.organization_id` is `None`, the code immediately raises
a `SessionAdmissionError`. This occurs even if the caller provides a valid
`organization_id`. This behavior is inconsistent with the non-in-flight path, which
correctly allows the request by only raising an error if both the persisted and the
provided `organization_id` are `None`. This blocks legitimate requests for legacy
sessions.

Did we get this right? 👍 / 👎 to inform future reviews.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (6)
autogpt_platform/backend/backend/executor/scheduler_unit_test.py (1)

336-410: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the fail-closed reason, not only the absence of calls.

These tests assert only that mocks were not awaited. They pass if _execute_copilot_turn fails earlier for an unrelated reason, because every assertion is negative. Add one positive assertion, for example a caplog check for the swallowed authorization or lookup failure, so a regression that breaks the function before the tenancy gate is still detected.

Also applies to: 473-510

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@autogpt_platform/backend/backend/executor/scheduler_unit_test.py` around
lines 336 - 410, Strengthen both revoked-session tests around
_execute_copilot_turn by adding a positive caplog assertion that confirms the
expected authorization or tenancy-resolution failure was logged or swallowed.
Keep the existing not-awaited assertions, and ensure the assertion distinguishes
the SessionOrgMembershipRevoked failure from unrelated early exits.
autogpt_platform/backend/backend/executor/scheduler.py (1)

256-316: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the tenancy resolution into a named helper.

_execute_copilot_turn now spans about 105 lines and mixes session lookup, ownership validation, tenancy resolution, session creation, and dispatch. Extract the block that produces (organization_id, team_id) into a helper, for example _resolve_scheduled_tenancy(args, target_session). The coding guidelines require functions under about 40 lines: "Keep functions under ~40 lines; extract named helpers when a function grows longer".

As per coding guidelines.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@autogpt_platform/backend/backend/executor/scheduler.py` around lines 256 -
316, Extract the organization_id/team_id derivation and resolve_session_tenancy
call from _execute_copilot_turn into a named helper such as
_resolve_scheduled_tenancy(args, target_session). Have it return the resolved
tenancy tuple while preserving existing session metadata, fallback, and
no-organization behavior; replace the inline block with the helper call so
_execute_copilot_turn remains under the function-length guideline.

Source: Coding guidelines

autogpt_platform/backend/backend/blocks/autopilot.py (2)

542-543: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add a reason comment for this local import.

The repository guideline requires top-level imports and allows local imports only for lazy loading of heavy optional dependencies. The sibling lazy import at Lines 382-384 carries the comment # avoid circular import, which documents the exception. This new import of SessionAdmissionError has no such comment, so a later reader may hoist it and reintroduce the cycle.

📝 Proposed comment
-        from backend.copilot.sdk.session_waiter import SessionAdmissionError
+        from backend.copilot.sdk.session_waiter import (
+            SessionAdmissionError,  # avoid circular import
+        )

As per coding guidelines: "Use top-level imports only — avoid local/inner imports except for lazy imports of heavy optional dependencies like openpyxl".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@autogpt_platform/backend/backend/blocks/autopilot.py` around lines 542 - 543,
Add a concise reason comment directly above the local import of
SessionAdmissionError in the relevant autopilot flow, documenting that it must
remain local to avoid a circular import. Keep the existing import placement and
behavior unchanged.

Source: Coding guidelines


357-358: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document the new parameters in the docstring.

execute_copilot now accepts organization_id and team_id. The Args: block at Lines 371-377 lists prompt, system_context, session_id, max_recursion_depth, user_id, and permissions, but omits the two tenancy parameters.

📝 Proposed docstring addition
             user_id: Authenticated user ID.
+            organization_id: Organization that owns the turn, or None.
+            team_id: Team scope within the organization, or None.
             permissions: Optional capability filter restricting tools/blocks.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@autogpt_platform/backend/backend/blocks/autopilot.py` around lines 357 - 358,
Update the Args section of execute_copilot’s docstring to document the new
organization_id and team_id parameters, including their tenancy-related purpose
and optional nature, while leaving the existing parameter descriptions
unchanged.
autogpt_platform/backend/backend/copilot/tools/schedule_followup_test.py (1)

165-166: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assertions are correct. Add coverage for the fresh-chat fallback branch.

These lines cover the explicit-target branch of schedule_followup.py. The other branch at Lines 256 and 261 of schedule_followup.py falls back to the scheduling session's organization and team when session_id is omitted. No test asserts that fallback, so a regression that sends None for the fresh-chat sentinel would pass.

The existing test that calls _execute(..., session_id=None) can assert the same two kwargs against the current session's tenancy.

Also applies to: 187-188

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@autogpt_platform/backend/backend/copilot/tools/schedule_followup_test.py`
around lines 165 - 166, Add coverage for the fresh-chat fallback in the existing
`_execute(..., session_id=None)` test: assert that the scheduling call receives
the current session’s organization and team IDs rather than `None`. Keep the
explicit-target assertions unchanged, and verify both tenancy kwargs passed
through the fallback branch.
autogpt_platform/backend/backend/copilot/tools/sub_session_test.py (1)

246-258: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assertions are correct. The resume branch stays uncovered.

This test exercises the fresh-sub-session branch, where the inner session inherits the parent tenancy. The resume branch (sub_autopilot_session_id set) also forwards the parent tenancy while the inner session row may carry different values. Add a test that resumes an owned session with a different organization and asserts which tenancy reaches run_copilot_turn_via_queue.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@autogpt_platform/backend/backend/copilot/tools/sub_session_test.py` around
lines 246 - 258, Add a test alongside the existing RunSubSessionTool tenancy
test that sets sub_autopilot_session_id to resume an owned inner session with
organization_id differing from the parent, then assert
run_copilot_turn_via_queue receives the parent organization and team tenancy.
Keep the existing fresh-sub-session assertions unchanged and specifically cover
the resume branch.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@autogpt_platform/backend/backend/api/features/chat/routes.py`:
- Around line 1451-1459: Update both pending-message checks in chat routes.py at
lines 1451-1459 and 1808-1814 to remove the session.organization_id is None
condition, so untagged and personal sessions are not rejected. Retain the 409
response only when an organization-scoped session has a persisted team while
turn_team_id is missing, and add regression tests covering both endpoints.

---

Nitpick comments:
In `@autogpt_platform/backend/backend/blocks/autopilot.py`:
- Around line 542-543: Add a concise reason comment directly above the local
import of SessionAdmissionError in the relevant autopilot flow, documenting that
it must remain local to avoid a circular import. Keep the existing import
placement and behavior unchanged.
- Around line 357-358: Update the Args section of execute_copilot’s docstring to
document the new organization_id and team_id parameters, including their
tenancy-related purpose and optional nature, while leaving the existing
parameter descriptions unchanged.

In `@autogpt_platform/backend/backend/copilot/tools/schedule_followup_test.py`:
- Around line 165-166: Add coverage for the fresh-chat fallback in the existing
`_execute(..., session_id=None)` test: assert that the scheduling call receives
the current session’s organization and team IDs rather than `None`. Keep the
explicit-target assertions unchanged, and verify both tenancy kwargs passed
through the fallback branch.

In `@autogpt_platform/backend/backend/copilot/tools/sub_session_test.py`:
- Around line 246-258: Add a test alongside the existing RunSubSessionTool
tenancy test that sets sub_autopilot_session_id to resume an owned inner session
with organization_id differing from the parent, then assert
run_copilot_turn_via_queue receives the parent organization and team tenancy.
Keep the existing fresh-sub-session assertions unchanged and specifically cover
the resume branch.

In `@autogpt_platform/backend/backend/executor/scheduler_unit_test.py`:
- Around line 336-410: Strengthen both revoked-session tests around
_execute_copilot_turn by adding a positive caplog assertion that confirms the
expected authorization or tenancy-resolution failure was logged or swallowed.
Keep the existing not-awaited assertions, and ensure the assertion distinguishes
the SessionOrgMembershipRevoked failure from unrelated early exits.

In `@autogpt_platform/backend/backend/executor/scheduler.py`:
- Around line 256-316: Extract the organization_id/team_id derivation and
resolve_session_tenancy call from _execute_copilot_turn into a named helper such
as _resolve_scheduled_tenancy(args, target_session). Have it return the resolved
tenancy tuple while preserving existing session metadata, fallback, and
no-organization behavior; replace the inline block with the helper call so
_execute_copilot_turn remains under the function-length guideline.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 33f9858d-37c2-49cd-ac43-5ce03d1a4bac

📥 Commits

Reviewing files that changed from the base of the PR and between f6b98c5 and d1632a8.

📒 Files selected for processing (23)
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/api/features/chat/routes_test.py
  • autogpt_platform/backend/backend/api/features/orgs/db.py
  • autogpt_platform/backend/backend/api/features/orgs/model.py
  • autogpt_platform/backend/backend/blocks/autopilot.py
  • autogpt_platform/backend/backend/blocks/autopilot_permissions_test.py
  • autogpt_platform/backend/backend/blocks/test/test_autopilot.py
  • autogpt_platform/backend/backend/copilot/executor/processor.py
  • autogpt_platform/backend/backend/copilot/executor/processor_test.py
  • autogpt_platform/backend/backend/copilot/sdk/session_waiter.py
  • autogpt_platform/backend/backend/copilot/sdk/session_waiter_test.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/copilot/tools/run_sub_session.py
  • autogpt_platform/backend/backend/copilot/tools/schedule_followup.py
  • autogpt_platform/backend/backend/copilot/tools/schedule_followup_test.py
  • autogpt_platform/backend/backend/copilot/tools/sub_session_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/data/db_manager.py
  • autogpt_platform/backend/backend/executor/scheduler.py
  • autogpt_platform/backend/backend/executor/scheduler_unit_test.py
  • autogpt_platform/frontend/src/app/api/openapi.json
🚧 Files skipped from review as they are similar to previous changes (4)
  • autogpt_platform/backend/backend/copilot/session_tenancy_test.py
  • autogpt_platform/backend/backend/api/features/orgs/model.py
  • autogpt_platform/backend/backend/data/db_manager.py
  • autogpt_platform/backend/backend/copilot/session_tenancy.py

Comment on lines +1451 to +1459
if turn_in_flight and (
session.organization_id is None
or (session.team_id is not None and turn_team_id is None)
):
raise HTTPException(
status_code=409,
detail=SESSION_TEAM_REVOKED_PENDING_DETAIL,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not reject pending messages for untagged sessions.

Line 1452 and Line 1808 return 409 for every active session where organization_id is None. This blocks pending follow-ups for legacy untagged and personal sessions. Keep the 409 only for an organization-scoped session whose persisted team is stale. Add regression tests for both endpoints.

  • autogpt_platform/backend/backend/api/features/chat/routes.py#L1451-L1459: remove the session.organization_id is None condition.
  • autogpt_platform/backend/backend/api/features/chat/routes.py#L1808-L1814: remove the equivalent session.organization_id is None condition.
Proposed fix
-    if turn_in_flight and (
-        session.organization_id is None
-        or (session.team_id is not None and turn_team_id is None)
-    ):
+    if (
+        turn_in_flight
+        and session.organization_id is not None
+        and session.team_id is not None
+        and turn_team_id is None
+    ):
-    if session.organization_id is None or (
-        session.team_id is not None and pending_team_id is None
-    ):
+    if (
+        session.organization_id is not None
+        and session.team_id is not None
+        and pending_team_id is None
+    ):
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if turn_in_flight and (
session.organization_id is None
or (session.team_id is not None and turn_team_id is None)
):
raise HTTPException(
status_code=409,
detail=SESSION_TEAM_REVOKED_PENDING_DETAIL,
)
if (
turn_in_flight
and session.organization_id is not None
and session.team_id is not None
and turn_team_id is None
):
raise HTTPException(
status_code=409,
detail=SESSION_TEAM_REVOKED_PENDING_DETAIL,
)
Suggested change
if turn_in_flight and (
session.organization_id is None
or (session.team_id is not None and turn_team_id is None)
):
raise HTTPException(
status_code=409,
detail=SESSION_TEAM_REVOKED_PENDING_DETAIL,
)
if (
session.organization_id is not None
and session.team_id is not None
and pending_team_id is None
):
raise HTTPException(
status_code=409,
detail=SESSION_TEAM_REVOKED_PENDING_DETAIL,
)
📍 Affects 1 file
  • autogpt_platform/backend/backend/api/features/chat/routes.py#L1451-L1459 (this comment)
  • autogpt_platform/backend/backend/api/features/chat/routes.py#L1808-L1814
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@autogpt_platform/backend/backend/api/features/chat/routes.py` around lines
1451 - 1459, Update both pending-message checks in chat routes.py at lines
1451-1459 and 1808-1814 to remove the session.organization_id is None condition,
so untagged and personal sessions are not rejected. Retain the 409 response only
when an organization-scoped session has a persisted team while turn_team_id is
missing, and add regression tests covering both endpoints.

Comment on lines +253 to +258
# A legacy row has no persisted tenant anchor. Its server-produced caller
# context is required so the processor can revalidate it before execution.
if persisted_session.organization_id is None and organization_id is None:
raise SessionAdmissionError("session_tenancy_unverifiable")
if organization_id is None:
team_id = None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Queued turn admission treats caller-supplied tenancy as authoritative instead of the persisted session row. The in-flight branch resolves tenancy from persisted_session, but the idle branch forwards the caller's organization_id and team_id straight to schedule_turn. Callers that pass a different organization, or None, therefore override the session's own tenancy.

  • autogpt_platform/backend/backend/copilot/sdk/session_waiter.py#L253-L258: anchor on persisted_session.organization_id and persisted_session.team_id when they are set, and raise session_tenancy_unverifiable only when both the persisted row and the caller supply no organization.
  • autogpt_platform/backend/backend/copilot/tools/run_sub_session.py#L186-L187: for the resume branch, pass the tenancy of the resumed owned session rather than the parent session, or rely on the corrected anchoring in run_copilot_turn_via_queue.

If CoPilotProcessor._execute_async already re-derives tenancy from the persisted row before execution, document that contract at both sites instead of changing behavior.

📍 Affects 2 files
  • autogpt_platform/backend/backend/copilot/sdk/session_waiter.py#L253-L258 (this comment)
  • autogpt_platform/backend/backend/copilot/tools/run_sub_session.py#L186-L187

@github-actions github-actions Bot added the cla: pending CLA not yet signed by all contributors label Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

batch:orgs batch-bot batch membership cla: pending CLA not yet signed by all contributors platform/backend AutoGPT Platform - Back end platform/blocks platform/frontend AutoGPT Platform - Front end size/xl

Projects

Status: 🚧 Needs work
Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants