Org layer and per-user persona injected into every chat system prompt

Chris-o-O added a multi-tenant organization model and a per-user persona that gets prepended to every chat and project-chat system prompt. The persona carries user role, practice areas, jurisdictions, bar number, a personal custom instruction field, and a firm-level custom instruction from the org -- making the assistant context-aware of who it's talking to.

multi-tenantpersonas

Three new tables land in migration 002: organizations (slug, jurisdictions, practice areas, custom_instructions, language), organization_members (role: admin|member), and organization_invites (email-based, 7-day expiry). Six columns are added to user_profiles: org_id, user_role, practice_areas, user_custom_instructions, bar_number, and a defensive re-add of openrouter_api_key.

The persona logic sits in backend/src/lib/persona.ts. fetchPersonaContext joins the profile and org; buildPersonaSystemPrompt formats the result. Both routes/chat.ts and routes/projectChat.ts prepend it to every system prompt. The fetch happens on every chat request with no caching.

On the API side, routes/organizations.ts (444 lines) covers full CRUD, member management, and invite flows. Frontend gets a new account/company page and a "My Profile" section on the General account page.

A few things to verify before adopting. RLS policies are SELECT-only -- the backend uses the service-role key for all writes, so write isolation relies entirely on your backend auth, not Postgres RLS. The org slug uniqueness helper uses Math.random() for the suffix with no collision loop. Member listing fetches user_profiles per member (N+1). The openrouter_api_key column appears in both migration 001 and 002 with if not exists guards, which signals the two features were developed independently without coordinating migration numbering.

So what Worth a look if you're building for law firms or teams where the assistant should know the user's role, practice area, and firm context. The persona module is isolated and testable. Adopt just `persona.ts` and the prompt injection if you don't need the full org management UI.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

2 commits from Chris-o-O/mike-explor, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
a64edcdc Merge pull request #1 from Chris-o-O/claude/add-features-rEBxK Chris-o-O 2026-05-07 ↗ GitHub
Add organizations, semantic search, and user personas
4f16dc2b feat: personas, company/team management, organisation context injection Claude 2026-05-08 ↗ GitHub
commit body
- Company & Team tab: create/join/edit orgs, manage members and invites, admin roles
- My Profile section in General tab: role, practice areas, bar number, personal AI instructions
- DB: organizations, organization_members, organization_invites tables + user_profiles persona columns
- Persona context injected into every chat and project chat system prompt
- REST API: /organizations/* CRUD + /user/persona GET/PATCH
- Textarea UI component

https://claude.ai/code/session_01NJxa5NasJ9qhaMkzEyqr2u

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-211.md from inside the repo you want the changes in.

⬇ Download capture-thread-211.md