Personal Drive/RAG must work without a company (null company_id) #55

Closed
opened 2026-08-02 03:33:48 -07:00 by westfarn · 0 comments
Owner

Summary

Connecting a personal Google Drive / OneDrive fails when the user has no company:

psycopg2.errors.NotNullViolation: null value in column "company_id" of relation "chat_backend_driveconnection" violates not-null constraint

Rejecting the link with no_company is not the fix. Personal Drive / personal RAG must work for users who are not part of a company. Company is only required for business (company) Drive / RAG.

Parent: #42
Related: #46 (personal vs company workspace), #47#49

Product model

Kind Who Company required? Workspace
personal any entitled user No personal DocumentWorkspace owned by user
company company manager Yes company DocumentWorkspace

Solo / no-company users must be able to connect personal Google Drive or OneDrive and sync into personal RAG.

Actual (bug)

  • DriveConnection.company is NOT NULL
  • upsert_drive_connection sets company=user.companyNULL → crash
  • Sync / document APIs / chat tenant scope assume a company workspace (ensure_company_workspace), so even a nullable FK alone is incomplete

Expected

  1. Personal Drive OAuth (intent=link_drive) succeeds for users with company_id=NULL
  2. Creates/updates DriveConnection(kind=personal, user=<user>, company=NULL)
  3. Sync / uploads for that path land in a personal workspace (user-scoped), not a company workspace
  4. RAG chat for no-company users resolves that personal workspace (fail closed; no cross-tenant leak)
  5. Company Drive (link_company_drive) still requires company + manager; reject clearly if missing

Suggested implementation

Schema

  • DriveConnection.companynull=True, blank=True
  • CheckConstraint: kind=companycompany_id IS NOT NULL; kind=personaluser_id IS NOT NULL
  • Replace uniqueness with conditional uniques:
    • personal: (user, provider) where kind=personal
    • company: (company, provider) where kind=company
  • DocumentWorkspace: allow personal ownership — user FK (nullable) + company nullable; company workspaces keep company set / user null; personal keep user set / company null

Runtime

  • upsert_drive_connection: personal may have null company; company kind still requires user.company_id (else OAuthError)
  • ensure_personal_workspace(user) + sync: personal connection → personal WS; company connection → company WS
  • resolve_chat_company_scope / document list-upload: if user has no company → personal workspace (do not raise company_missing for personal RAG)
  • Keep stove-pipe: scope always keys retrieval on the resolved workspace_id

Tests

  • Personal Drive callback with user.company=NULL → 302 success, row with company_id=NULL, user_id set
  • Company Drive still rejects no-company / non-manager
  • Sync personal no-company connection creates docs on personal workspace
  • Chat/RAG scope for no-company user returns personal workspace (no company_missing)

Out of scope / follow-ups

  • Full multi-workspace retrieval (personal and company KB in one chat turn) — may stay under #46
  • FE copy for no_company on company connect only

Repro

  1. User with company_id = NULL (entitled plan with allows_rag)
  2. Connect personal Google Drive / OneDrive
  3. Observe NotNullViolation (or wrong no_company rejection from the interim PR)

Anti-goals

  • Do not require inventing a fake company for personal Drive
  • Do not treat “reject no company” as closing this bug for personal connect
## Summary Connecting a **personal** Google Drive / OneDrive fails when the user has no company: ``` psycopg2.errors.NotNullViolation: null value in column "company_id" of relation "chat_backend_driveconnection" violates not-null constraint ``` **Rejecting** the link with `no_company` is **not** the fix. Personal Drive / personal RAG must work for users who are **not** part of a company. Company is only required for **business** (company) Drive / RAG. Parent: [#42](https://git.aimloperations.com/ai_ml_operations/chat_backend/issues/42) Related: [#46](https://git.aimloperations.com/ai_ml_operations/chat_backend/issues/46) (personal vs company workspace), [#47](https://git.aimloperations.com/ai_ml_operations/chat_backend/issues/47)–[#49](https://git.aimloperations.com/ai_ml_operations/chat_backend/issues/49) ## Product model | Kind | Who | Company required? | Workspace | |------|-----|-------------------|-----------| | `personal` | any entitled user | **No** | personal `DocumentWorkspace` owned by `user` | | `company` | company manager | **Yes** | company `DocumentWorkspace` | Solo / no-company users must be able to connect personal Google Drive or OneDrive and sync into **personal RAG**. ## Actual (bug) - `DriveConnection.company` is NOT NULL - `upsert_drive_connection` sets `company=user.company` → `NULL` → crash - Sync / document APIs / chat tenant scope assume a company workspace (`ensure_company_workspace`), so even a nullable FK alone is incomplete ## Expected 1. Personal Drive OAuth (`intent=link_drive`) succeeds for users with `company_id=NULL` 2. Creates/updates `DriveConnection(kind=personal, user=<user>, company=NULL)` 3. Sync / uploads for that path land in a **personal** workspace (user-scoped), not a company workspace 4. RAG chat for no-company users resolves that personal workspace (fail closed; no cross-tenant leak) 5. Company Drive (`link_company_drive`) still requires company + manager; reject clearly if missing ## Suggested implementation ### Schema - `DriveConnection.company` → `null=True, blank=True` - CheckConstraint: `kind=company` ⇒ `company_id IS NOT NULL`; `kind=personal` ⇒ `user_id IS NOT NULL` - Replace uniqueness with conditional uniques: - personal: `(user, provider)` where `kind=personal` - company: `(company, provider)` where `kind=company` - `DocumentWorkspace`: allow personal ownership — `user` FK (nullable) + `company` nullable; company workspaces keep `company` set / `user` null; personal keep `user` set / `company` null ### Runtime - `upsert_drive_connection`: personal may have null company; company kind still requires `user.company_id` (else `OAuthError`) - `ensure_personal_workspace(user)` + sync: personal connection → personal WS; company connection → company WS - `resolve_chat_company_scope` / document list-upload: if user has no company → personal workspace (do not raise `company_missing` for personal RAG) - Keep stove-pipe: scope always keys retrieval on the resolved `workspace_id` ### Tests - Personal Drive callback with `user.company=NULL` → 302 success, row with `company_id=NULL`, `user_id` set - Company Drive still rejects no-company / non-manager - Sync personal no-company connection creates docs on personal workspace - Chat/RAG scope for no-company user returns personal workspace (no `company_missing`) ## Out of scope / follow-ups - Full multi-workspace retrieval (personal **and** company KB in one chat turn) — may stay under [#46](https://git.aimloperations.com/ai_ml_operations/chat_backend/issues/46) - FE copy for `no_company` on **company** connect only ## Repro 1. User with `company_id = NULL` (entitled plan with `allows_rag`) 2. Connect personal Google Drive / OneDrive 3. Observe NotNullViolation (or wrong `no_company` rejection from the interim PR) ## Anti-goals - Do **not** require inventing a fake company for personal Drive - Do **not** treat “reject no company” as closing this bug for personal connect
westfarn changed title from DriveConnection upsert crashes: null company_id NotNullViolation on Google Drive link to Personal Drive/RAG must work without a company (null company_id) 2026-08-02 03:38:16 -07:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ai_ml_operations/chat_backend#55