You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#158 asks for collaborative ownership of resources (starting with write-shared Knowledge Bases for teacher groups). #400 proposes solving it by adding a team_id ownership dimension to six resource tables and teaching every permission path about teams.
This issue proposes an alternative that solves the same problems with a much smaller blast radius: model a team as a special creator-user rather than as a new ownership dimension. Full rationale and the side-by-side trade-off live in #400; this issue is the concrete plan of action.
Principle
A team is an owner, not a new kind of ownership. It is a Creator_users row scoped to one organization, with a real email, a real organization_id, and a backing OWI user. Because it satisfies every existing invariant — owner-email resolves to a creator-user-with-an-org — the completion pipeline, RAG, org-config resolution, OWI publication and group gating all keep working unchanged. The team can own assistants / KBs / rubrics / libraries, and can share and be shared with, exactly like any user.
Two access modes:
Edit team resources → "access as team T", a scoped and audited identity switch. No changes to can_access_*().
Use team resources → normal sharing (team-published assistants shared with members; OWI group membership synced on join/leave).
Wire up the OWI un-share path: _remove_users_from_owi_group() exists in assistant_sharing_service.py but is never called, and _sync_assistant_to_owi_group() only adds users. Removing a share must evict the user from the OWI group. Needed regardless of teams.
Phase 1 — team entity (backend, no UI)
teams satellite table (user_id PK → Creator_users.id, name, description, organization_id, timestamps) and team_members (team_id, user_id, role, joined_at).
Team creation provisions a Creator_users row (user_type = 'team' or an explicit flag), a synthetic namespaced email, no password login (auth_provider = 'team'), and a backing OWI user — promoted from the current best-effort mirror to fatal/retried, since a team without an OWI user cannot publish.
Team CRUD + membership management, scoped to org admins (and team admins for membership ops).
Phase 2 — "access as team" auth
POST /creator/teams/{id}/assume: verify the caller's membership server-side, mint a short-TTL scoped JWT with email = team_email and on_behalf_of = <real_user_id>.
Thread on_behalf_of into request logging so every as-team action records the real actor.
Frontend: a team switcher that stashes the member's own token, swaps in the team token, and shows a persistent "acting as Team T — back to me" control.
Phase 3 — membership → sharing fan-out
When a team publishes an assistant, share it with all current members; on member add/remove, add/remove the share (reuses assistant_sharing_service + the Phase 0-fixed OWI sync).
Same mechanism for any other resource type we decide should auto-share to members.
Phase 4 — LTI + polish
Verify the LTI activity configure flow surfaces team assistants for members (it should already, via the owned-or-shared list) and that student-launch completions resolve org correctly via the team owner.
Context
#158 asks for collaborative ownership of resources (starting with write-shared Knowledge Bases for teacher groups). #400 proposes solving it by adding a
team_idownership dimension to six resource tables and teaching every permission path about teams.This issue proposes an alternative that solves the same problems with a much smaller blast radius: model a team as a special creator-user rather than as a new ownership dimension. Full rationale and the side-by-side trade-off live in #400; this issue is the concrete plan of action.
Principle
A team is an owner, not a new kind of ownership. It is a
Creator_usersrow scoped to one organization, with a real email, a realorganization_id, and a backing OWI user. Because it satisfies every existing invariant — owner-email resolves to a creator-user-with-an-org — the completion pipeline, RAG, org-config resolution, OWI publication and group gating all keep working unchanged. The team can own assistants / KBs / rubrics / libraries, and can share and be shared with, exactly like any user.Two access modes:
can_access_*().Plan of action
Phase 0 — prerequisites (fix existing gaps)
update_assistant_proxywrite path must be guarded before we build on it._remove_users_from_owi_group()exists inassistant_sharing_service.pybut is never called, and_sync_assistant_to_owi_group()only adds users. Removing a share must evict the user from the OWI group. Needed regardless of teams.Phase 1 — team entity (backend, no UI)
teamssatellite table (user_idPK →Creator_users.id,name,description,organization_id, timestamps) andteam_members(team_id,user_id,role,joined_at).Creator_usersrow (user_type = 'team'or an explicit flag), a synthetic namespaced email, no password login (auth_provider = 'team'), and a backing OWI user — promoted from the current best-effort mirror to fatal/retried, since a team without an OWI user cannot publish.Phase 2 — "access as team" auth
POST /creator/teams/{id}/assume: verify the caller's membership server-side, mint a short-TTL scoped JWT withemail = team_emailandon_behalf_of = <real_user_id>.on_behalf_ofinto request logging so every as-team action records the real actor.Phase 3 — membership → sharing fan-out
assistant_sharing_service+ the Phase 0-fixed OWI sync).Phase 4 — LTI + polish
What this explicitly does NOT touch
team_idcolumns on resource tables.can_access_*().References