managarten/docs/plans/team-workbench.md
Till JS e8077a44b4 docs: flesh out Multi-Agent plan shipping log + Team Workbench forward plan
The Multi-Agent Workbench shipped end-to-end (commits 1771063df through
7c89eb625). This commit turns the plan doc into a proper history + post-
mortem and captures the deferred Team-Workbench as its own forward plan
so the architectural breadcrumbs don't rot.

docs/plans/multi-agent-workbench.md:
- Status bumped to  Shipped; every phase checkbox flipped.
- Open-questions section rewritten with the decisions that were
  actually made (name-unique via store write-time check, per-source
  system principalIds, policy fully migrated, scene binding default-
  empty with smart suggestion).
- New "Shipping-Historie" table mapping each phase to its commit, the
  number of files touched, and the test outcome.
- New "Lessons Learnt + Follow-Up Ideen" with:
  * What went better than expected (L3 Actor cutover, getOrCreate
    instead of unique index, displayName caching)
  * Thin spots worth revisiting (avatar not on Actor, missing token
    counter for budget, no missions list on agent detail, no
    drag-reassign, scene binding doesn't drive filters yet)
  * Five deferred follow-up projects (team features, agent memory
    self-update, agent-to-agent messaging, meta-planner, per-agent
    encryption domains)

docs/plans/team-workbench.md (NEW):
- Full forward-looking plan for the deferred Team-Workbench.
- Two use-cases (human multi-user vs multi-agent sharing team
  context) with the observation that they share the same infra.
- Decision candidates table (still open — meant as T0 RFC fodder,
  not baked in).
- Architecture sketch with data-model deltas over the current
  single-user shape.
- Encryption subsection dedicated to the hardest problems: team-key
  wrapping per member (reuses Mission-Grant pattern), member-removal
  rotation (lazy vs eager), Zero-Knowledge-mode incompatibility.
- T0..T6 phasing (~7 weeks for a clean first-pass).
- Section "Wie Multi-Agent dafür den Weg geebnet hat" enumerating
  the four invariants the shipped Phase 0-7 deliberately preserved
  to make this plan cheap when it lands.

docs/plans/README.md (NEW):
- Index doc with the AI/Workbench roadmap as an ASCII flow so future
  contributors can locate themselves in the sequence without reading
  three 400-line plans first.

docs/future/AI_AGENTS_IDEAS.md:
- Header marks Point 1 (encrypted tables) as shipped via the Mission
  Grant plan; points 2-8 stay relevant. Cross-link to all three plan
  docs so this stays the go-to backlog.

services/mana-ai/CLAUDE.md:
- Design-context header expanded to link to all four related docs
  (arch §20-22, both shipped plans, forward team plan, ideas backlog).

No code changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 22:17:04 +02:00

250 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Plan: Team Workbench — Multi-User-Workbench mit geteiltem AI-Kontext
**Status:** Forward-looking Plan, nicht gestartet. 2026-04-15.
**Scope:** Erweitere den bestehenden Single-User-Multi-Agent-Workbench so, dass mehrere User einen gemeinsamen "TeamSpace" bewohnen: geteilte Daten, geteilte Agenten, geteilte Missions-Queue. Admin-Sicht auf Team-Mitglieder.
**Abhängigkeiten:** Baut direkt auf [`multi-agent-workbench.md`](./multi-agent-workbench.md) auf — insbesondere die identity-aware `Actor`-Shape und die Agent-als-Bürger-Abstraktion. Ohne diese beiden Foundations wäre dieser Plan 3× so groß.
**Verwandte Docs:** [`docs/architecture/COMPANION_BRAIN_ARCHITECTURE.md`](../architecture/COMPANION_BRAIN_ARCHITECTURE.md) §20§22, [`ai-mission-key-grant.md`](./ai-mission-key-grant.md) (Key-Wrapping-Muster), [`docs/future/AI_AGENTS_IDEAS.md`](../future/AI_AGENTS_IDEAS.md).
---
## Die zwei Use-Cases
Der Plan deckt zwei verwandte Szenarien, die sich denselben Unterbau teilen:
### A) Team-Workspace (menschliche Mitbenutzer)
Mehrere User teilen einen Workspace. Ein Admin kann durch die Workbench-Scenes _seiner Teammitglieder_ blättern und sieht, woran jeder gerade arbeitet. Gemeinsame Notizen / Tasks / Kalender synchronisieren team-weit.
**Mini-Beispiel:** Till + Anna im gleichen TeamSpace. Anna schreibt ein Note. Till sieht es in seinem Notes-Modul mit Autor-Avatar "Anna". Till hat eine Admin-Lens "Workbench von Anna" und sieht, was sie gerade offen hat.
### B) Multi-Agent mit geteiltem Team-Kontext (AI-Agenten als Team-Mitglieder)
10 benannte Agenten arbeiten autonom, teilen sich aber ein gemeinsames Wissen. Jeder Agent kann Team-scope Dinge lesen/schreiben; manche Dinge bleiben privat pro Agent.
**Mini-Beispiel:** "Cashflow Watcher" legt einen Event "Rechnung fällig am 15." ins Team-Kalender. "Travel Planner" sieht beim nächsten Tick die Rechnung, plant Reisekosten drumherum. Beide greifen auf denselben Team-Kontext zu.
**Der Trick:** A und B sind die gleiche Infrastruktur, nur mit unterschiedlichen `Principal.kind` Werten. Das ist möglich weil Phase 1 des Multi-Agent-Plans die `Actor.principalId`-Abstraktion eingeführt hat.
---
## Entscheidungen zum Diskutieren (noch nicht fix)
Diese Tabelle ist bewusst **nicht** "baked in" — sie ist Diskussionsgrundlage für einen RFC vor Implementierung. Im Unterschied zum Multi-Agent-Plan, wo wir vor-live-Freiraum hatten, geht dieser Plan dann produktiv und bricht teils existierende Crypto-Garantien auf.
| Frage | Kandidat | Risiko / Trade-off |
|---|---|---|
| **TeamSpace-Objekt** | Neues `teamSpaces` Dexie-Table + pgSchema mit RLS auf `team_id` | Neue Entität → Touch-Points in Sync, Modul-Registry, UI-Modell. Aber: klares Schema, eindeutige Eigentumsverhältnisse. |
| **Mitgliedschaft** | `teamMembers (teamId, userId, role, addedAt)` + server-side enforce | Auth-Service braucht Team-Awareness. Memoro hat schon das Muster. |
| **Rollen** | `admin` / `member` / `viewer` Minimal-Set | Mehr Rollen = mehr RLS-Komplexität. 3 reichen für die meisten echten Fälle. |
| **Principal-Kind-Erweiterung** | `Actor.kind = 'user'\|'ai'\|'system'\|'team'`? oder `kind` bleibt, `teamId` wird ein separates Feld? | Bevorzugen: `teamId?: string` als zusätzliches Feld am Actor statt neuer kind. Compat-leicht. |
| **Record-Scoping** | `record.teamId?: string` neben `record.userId` — Sync-Router dispatcht basierend auf Scope | Braucht Mode-Flag pro Modul ("ist diese Tabelle team-fähig?"). Notes, Tasks, Calendar sind offensichtliche Kandidaten; Playground-Snippets wahrscheinlich nicht. |
| **Encryption** | Per-Team-Key, wrapped-per-member analog zu Mission-Grants | Größter Risiko-Punkt. Key-Rotation beim Mitglied-Ausschluss ist non-trivial. Siehe Detail-Abschnitt unten. |
| **Mission-Ownership** | `mission.teamId?` + `mission.ownerUserId` + `mission.agentId` | Frage: können _nur_ Admins team-Missions anlegen, oder jedes Mitglied? Empfehlung: jedes Mitglied kann team-Missions anlegen, Agent-Grants pro Mission wie gehabt. |
| **Agent-Scope** | Agent hat optional `teamId` — ein Agent existiert entweder privat (User-scope) oder team-weit | Alternativen wären komplizierter (Agent in mehreren Teams) und lösen keine echten User-Needs. |
| **Scene-Scope** | Scenes bleiben per-User. Admin-Lens ist eine read-only Projektion auf andere Mitgliedspezifische Scenes. | Scenes team-weit shared hat keinen klaren UX-Wert — jeder hat seinen Arbeitsplatz. |
| **Admin-Lens** | Neuer "Team-Workbench" App-Tab. Admin sieht Tile-Wall der aktiven Scenes aller Mitglieder (Overview + Click-to-Inspect). | Kein versteckter Lens-Modus — immer klar wessen Sicht man gerade hat (Breadcrumb). |
---
## Architektur-Skizze
```
┌─────────────────────────────────────────────────────────────┐
│ Webapp (Dexie) — scopes multiplied │
│ │
│ Records carry {userId, teamId?} — sync router fan-out │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Private scope │ │ Team scope │ │
│ │ userId=me │ │ teamId=team-xyz │ │
│ │ my notes/tasks │ │ shared notes │ │
│ └──────────────────┘ └──────────────────┘ │
└────────────────┬───────────────────┬────────────────────────┘
│ │
▼ ▼
┌────────────┐ ┌────────────┐
│ mana-sync │ │ mana-sync │
│ user-scope │ │ team-scope │ ← RLS on team_id
│ RLS │ │ + team- │
│ │ │ membership │
└────────────┘ └────────────┘
┌────────────────┐
│ mana-auth │
│ teams + │
│ memberships + │
│ team-KEK │
└────────────────┘
```
### Datenmodell (Skizze)
```ts
// mana-auth
interface TeamSpace {
id: string;
ownerUserId: string;
name: string;
createdAt: string;
// Team KEK: ein Master-Key pro Team, gewrapped mit jedem Mitglied-
// UserKey. Analog zu Mission-Grants aber auf Team-Ebene.
teamMasterKeyWrapped: Record<userId, WrappedKey>;
}
interface TeamMember {
teamId: string;
userId: string;
role: 'admin' | 'member' | 'viewer';
addedAt: string;
addedBy: string;
}
// Shared-AI: Actor gets a third dimension
interface Actor {
kind: 'user' | 'ai' | 'system';
principalId: string;
displayName: string;
teamId?: string; // NEW — present when the write is
// team-scoped; absent for private writes.
// ... rest as before
}
// Webapp: Mission + Agent optionally team-scoped
interface Agent {
// ... existing fields
teamId?: string; // NEW
}
interface Mission {
// ... existing fields
teamId?: string; // NEW
}
// Webapp: Scenes stay per-user, but gain an admin-lens feature
interface WorkbenchScene {
// ... existing fields (NOT changed)
}
interface AdminLensState { // NEW — per-device localStorage
activeView: 'self' | { kind: 'member'; userId: string; teamId: string };
}
```
---
## Encryption-Design — der härteste Teil
Das Encryption-Modell ist der größte Posten und verdient einen eigenen Unter-Plan. Drei Hauptfragen:
### Frage E1 — Wie teilen Mitglieder einen Schlüssel?
**Kandidat: Team-KEK, wrapped-per-member.**
- Team hat einen symmetrischen `teamKey` (AES-GCM-256).
- Jedes Mitglied hat ihre eigenen `teamKeyWrappedWith_userPubKey` — eine Kopie pro Mitglied, via RSA-OAEP oder X25519-ECDH gewrapped.
- Mitglied-Hinzufügen = existierender Admin wrapped den Team-Key mit dem Public-Key des neuen Mitglieds, pusht das ins `teamMasterKeyWrapped` Map.
- Mitglied-Entfernen = **komplette Key-Rotation** (siehe E3).
Das ist exakt das Muster aus [Mission-Grants](./ai-mission-key-grant.md), eine Ebene höher. Wir haben das Wrapping-Toolkit schon.
### Frage E2 — Welche Records werden per Team-Key verschlüsselt?
Die bestehende `crypto/registry.ts` definiert User-scope Verschlüsselung. Für Team-Records kommt eine **zweite Registry-Ebene**: `teamEncryptionRegistry` mit den Feldern die unter Team-Scope encrypted sind. In der Praxis oft identisch mit der User-Registry (gleiche Tabellen, gleiche Felder), aber unter dem Team-Key statt User-Key.
### Frage E3 — Mitglied-Entfernung + Key-Rotation
Das ist der Kletterberg. Wenn Bob rausgeworfen wird:
- Alles was er _bisher_ gesehen hat, kann er Cache-seitig behalten (unfixable without TPM).
- Neue Writes dürfen nicht mehr mit dem alten Team-Key verschlüsselt werden.
- → Neuer `teamKey` generieren, alle existierenden Team-Records re-encrypten.
**Das ist teuer.** Bei einem Team mit 10k Notes + 5k Tasks ist Re-Encryption eine Minutes-lange Operation. Optionen:
- **Eager**: synchron im Hintergrund nach Mitglied-Entfernung, mana-ai service macht die Arbeit.
- **Lazy**: Team-Key-Version wird hochgezählt. Alte Records bleiben encrypted unter alter Version; bei jedem Read mit neuem Key failt es, client re-encrypts und sync-pusht. Über Zeit wandert alles auf die neue Version. Mitglied-Ausschluss wirkt "sofort" für Writes, "eventually" für alte Records.
Empfehlung: **Lazy mit Fortschritts-Anzeige**. Stoppt die UI nicht, ist aber für Admin erkennbar ("23% noch auf alter Schlüsselversion").
### Frage E4 — Zero-Knowledge-Mode im Team-Kontext?
Nicht supported. Sobald ein User in einem Team ist, muss mana-auth den Team-Key wrappen können — das geht nur wenn der User _nicht_ Zero-Knowledge ist. Team-Beitritt fragt explizit: "Für Team-Beitritt wird dein Vault aus Zero-Knowledge genommen." Vermutung: akzeptabel. Teams sind per se geteiltes Modell.
---
## Phasen (grob)
Diese Phasierung ist eine erste Skizze — vor Implementierung braucht's einen eigenen RFC.
### T0 — RFC + ADR (1 Woche)
- Dieses Dokument schärfen; Encryption-Frage E1-E4 durch-entscheiden
- ADR "Team-Scope-Data" in `docs/decisions/`
- Schema-Entwürfe in `services/mana-auth/sql/` + `services/mana-sync/`
### T1 — Team-Entität + Membership (1 Woche)
- `teams` Tabelle in mana-auth mit RLS
- `POST /api/v1/teams` create + `POST /api/v1/teams/:id/members` invite
- Minimal-UI: Settings → Teams → "Team anlegen" + Member-List
### T2 — Team-Scope Records, ohne Encryption (1 Woche)
- `record.teamId?` an Records der team-fähigen Tabellen (notes, tasks, events, …)
- Sync-Router fan-out: ein Record mit `teamId` → POST /sync/team/:teamId statt /sync/user/:userId
- `mana-sync` Backend: neue Sync-Channel für Team, RLS auf team-membership
- UI: "Als Team-Note speichern" Toggle in den Create-Flows von notes, tasks, events
An diesem Punkt haben wir Team-geteilte Daten in Plaintext — bewusst ohne Crypto zuerst, um das Datenmodell zu validieren bevor Encryption-Komplexität dazukommt.
### T3 — Team-Encryption (2 Wochen)
- Team-KEK in mana-auth mit wrapped-per-member
- webapp: `getTeamKey(teamId)` Cache, `encryptTeamRecord` / `decryptTeamRecord` in crypto-Layer
- Registry: `teamEncryptionRegistry` mit den encrypted Feldern
- Member-Einladung-Flow: admin wraps team-key with new member's pub-key
- Member-Removal-Flow: lazy-rotation mit Version-Bumping + Background-Re-Encrypt
### T4 — Actor-Erweiterung + AI-Integration (1 Woche)
- `Actor.teamId?` Feld in shared-ai (additiv, compat-leicht)
- Mission-runner checkt bei team-scope mission ob owning agent team-scope ist
- Proposal + Workbench-Timeline zeigen "von User X im Team Y" statt nur "von Agent Z"
### T5 — Admin-Lens (1 Woche)
- Neues Modul `team-workbench` — Admin sieht Tile-Wall der aktiven Scenes der Teammitglieder (ohne Mutations-Rechte standardmäßig)
- Per-Member "View as Alice" Modus für Admins (breadcrumb klar sichtbar: "Du siehst die Scene von Alice")
- Context-Menu auf Members: "Message", "Remove from team"
### T6 — Polish + Rollout (1 Woche)
- Feature-Flag `PUBLIC_TEAM_WORKBENCH`
- Docs: security.mdx Abschnitt "Teams und geteilte Verschlüsselung"
- Prometheus alerts auf team-level failures
- Onboarding-Flow: "Willst du ein Team erstellen?"
**Gesamtaufwand:** ~7 Wochen für eine saubere Erstfassung. Die Hälfte davon ist Encryption + Migration-Robustheit.
---
## Wie Multi-Agent dafür den Weg geebnet hat
Die Multi-Agent-Phase hat bewusst vier Invarianten etabliert, die Team-Features später billig machen:
1. **`Actor.principalId` ist universell.** User, Agent, System sitzen alle auf derselben Achse. Team wird zu einem _context-Feld_ am Actor, nicht zu einem vierten `kind`. Keine discriminated-union-Explosion.
2. **`Actor.displayName` ist cached.** Wenn Alice das Team verlässt, zeigen historische Events trotzdem "Alice". Keine Need-To-Join auf aktuelle Membership-Tabellen.
3. **Scene ↔ Agent ist orthogonal (Lens, nicht Scope).** Die gleiche Idee lässt sich auf Team-Membership anwenden: "Admin-View auf Alice" ist eine Lens, keine Scope-Änderung. Lässt sich oben draufsetzen ohne Scene-Modell zu ändern.
4. **Agent-Memory ist encrypted-at-rest wie Notes.** Das Pattern ist etabliert. Team-Encryption nutzt denselben Pfad (wrapValue/unwrapValue), nur mit einem anderen Key.
Konkret: das L3-Actor-Cutover-Refactor würden wir für Team-Features nochmal machen müssen, wenn wir's nicht jetzt gemacht hätten. Jetzt ist es ein 3-zeiliger Change (`teamId?: string` dazu).
---
## Explizite Nicht-Ziele (auch für T-Phasen)
- **Keine public/open Teams.** Invite-only, auch langfristig. Public-Feeds sind ein völlig anderes Produkt.
- **Keine Real-Time-Presence.** "Alice tippt gerade in Note X" ist ein Follow-Up — Team-Sync bleibt beim etablierten pull/push-Modell.
- **Keine Team-weiten Agent-Mutations durch Admin.** Admin kann sehen was Alice's Agents tun, aber nicht deren Policy ändern. Respektiert Autonomie.
- **Keine Cross-Team-Migration.** Eine Mission/Note ist entweder privat oder team-scoped. Umhängen von Team zu Team ist nicht vorgesehen — wenn User's es brauchen, ist's ein Duplicate-Create.
---
## Offene Fragen (für den T0 RFC)
1. **Owner-Konzept:** Wer besitzt ein Team? Kann Ownership übertragen werden? Was passiert wenn der Owner geht?
2. **Abrechnung:** Mana-Credits sind heute per-User. Teams haben geteilte LLM-Kosten — wer zahlt? Pro-Team-Subscription vs. Pro-Member-Subscription?
3. **Viewer-Rolle:** hat Viewer Write-Access auf Proposals (approve reject)? Oder nur Read? Vermutlich nur Read.
4. **Mission-Rights:** Darf ein Member eine Mission auf einem shared agent starten, die der Admin nicht approved hat? Oder brauchen alle Team-Missions Admin-Approval?
5. **Guest-Mode:** Team-Einladung per Magic-Link für noch-nicht-registrierte User — supported oder nicht? (Vermutlich in T1 nicht, später optional.)
6. **Conflict-Resolution auf Team-Records:** Alice editiert Note A, Bob auch. LWW wie bei User-Records oder expliziter Merge-Editor? Vermutlich LWW fürs Erste; Merge-Editor ist ein Folgeprojekt.
7. **Mobile App:** Mobile läuft hinter der Webapp. Wie lange ist mobile ohne Team-Features akzeptabel? Oder Hand-off via Magic-Link auf Web?