# Design — Iteration 3: Multi-team workspaces (Groups + users + per-team prompts)

Date: 2026-06-29
Status: approved (design), pending spec review
Builds on Iteration 2 (`2026-06-29-zammad-email-drafting-design.md`).

## 1. Goal & scope

Stand up **per-department email-drafting assistants** with real isolation: each team has its own
Open WebUI Group, its own Model + system prompt, and sees **only its own emails**. Used to test
per-team email responses. Engine stays `claude-sonnet-4-6` on fake data (sovereignty unchanged).

**Decisions locked in brainstorming:**

| Decision | Choice |
|----------|--------|
| Multi-user depth | **Real accounts + Open WebUI Groups + access control** (per-team Model visibility) |
| Teams | **Full department set** (6): Sales, Quotation, Design, Logistics, Accounting, After-sales |
| Email scope per team | **Each team sees only its own emails** |
| Email → team mapping | **Native Zammad groups** (one group per department; tickets belong to a group) |

## 2. Teams & email-type routing

Six customer-facing departments, mapped from the client's email-type sheet:

| Team (Zammad group) | Email types it handles |
|---------------------|------------------------|
| **Sales** | new brand / basic info, interested designs, online-meeting requests, general inquiries |
| **Quotation** | new designs for quotation, updated quotation at current metal price, reorders |
| **Design** | new designs for development, prepare photos of similar items, CAD/3D review |
| **Logistics** | order status / on-time, expedite, lead-time-to-deliver, shipping |
| **Accounting** | invoice / payment questions, proforma invoice (PI) |
| **After-sales** | quality complaints, warranty/RMA, order cancellations |

Marketing (forwards the PI to the customer in the reorder flow) is deferred; can be added as a 7th
team with the same pattern.

## 3. Architecture

### 3.1 Zammad side
- Create the **6 groups** via the admin API (idempotent: search-before-create).
- Extend `seed/seed_zammad.py`: ensure the groups exist, **assign each seeded ticket to its
  department group** (currently all land in the default `Users` group), and add ~1 more inbound
  email per department so every team has ≥2 emails to test. Ticket creation already supports
  `group`; the seeder's ticket table gains a `team` field that maps to the group name.

### 3.2 `mail_reader.py` change
- Add a Valve **`ZAMMAD_GROUP`** (str, default `""` = no filter).
- When set, `list_recent_emails` appends `group.name:"<ZAMMAD_GROUP>"` to the search query, so the
  tool only ever returns that group's open emails. `get_email_thread` is unchanged (resolving a
  number a team can see; a team's prompt only surfaces numbers from its own list).
- This is the **only** code change; the tool stays read-only (GET only).

### 3.3 Open WebUI side (per team)
For each of the 6 teams:
1. **Group** `<Team>` (e.g. "Sales").
2. **Mail Reader tool instance** `mail_reader_<team>` — same code, Valve `ZAMMAD_GROUP=<team>` +
   the shared `ZAMMAD_TOKEN`. (Per-tool Valves are global, so a separate instance per team is how
   the group filter is bound per team.)
3. **Model** `<Team> Email Assistant` — department system prompt, attaches `mail_reader_<team>`,
   `Function Calling = Native`. **Access control: visible only to the `<Team>` Group.**
4. **Users**: 1 test account per team (created by the human — see §5), added to the `<Team>` Group.

### 3.4 Isolation analysis
A user is in exactly one team Group. Open WebUI access control shows them only their team's Model.
That Model attaches only their team's Mail Reader, whose `ZAMMAD_GROUP` Valve is fixed (admin-only,
the user can't change it). `list_recent_emails` exposes **no group parameter**, so a user cannot
request another department's emails. The shared admin Zammad token is never exposed to users.

This gives **real user-facing isolation**. Defense-in-depth (out of scope now): give each team a
Zammad agent restricted to its group and use that agent's token in the team's tool Valve, so
isolation also holds at the Zammad API layer.

### 3.5 Per-team prompts
Each department prompt = the Iteration-2 base (read the thread via the tool, never invent facts,
`[placeholder: …]`, English, Subject+Body, read-only) **plus** a department specialization:
- **Sales** — warm, relationship-building, qualifies new leads, proposes next steps/meetings.
- **Quotation** — precise, structured quote, references current metal/silver rate, clear validity.
- **Design** — references design refs/SKUs, samples/photos, development timelines.
- **Logistics** — status/ETA focused, expedite handling, shipping/Incoterms.
- **Accounting** — invoice/PI/payment terms, professional and exact, no financial advice.
- **After-sales** — empathetic, complaint/RMA handling, concrete resolution options.

## 4. Build order
Because each team is 6× manual Open WebUI wiring (tool + model + group + ACL), build and **validate
one team end-to-end first (Sales)**, confirm isolation, then replicate the other five. Avoids cloning
a mistake six times.

## 5. Users & permissions boundary
Creating accounts and setting passwords is a human action (the assistant does not create accounts or
enter passwords). The human creates 1 test account per team in the admin panel; the assistant then
adds each account to its team Group and does all Group/Model/Tool/ACL setup.

## 6. Testing
Per team, after setup:
1. Log in as that team's user → **only that team's Model** is visible.
2. *"List the recent emails"* → shows **only that department's** emails.
3. *"Draft a reply to <one of them>"* → reads the thread, drafts in the department's style.
4. **Cross-check isolation**: the Sales user sees neither the Logistics Model nor Logistics emails.

## 7. Out of scope / future
- Per-team Zammad agent tokens (API-layer isolation hardening).
- Marketing (7th team) and any further departments.
- Classification/auto-routing of incoming emails into department groups (here the seeder assigns
  groups; real Zammad would route via triggers/filters).
- Write operations, Ollama engine, the separate client-side DB.
