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

16 KiB
Raw Permalink Blame History

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 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 §20§22, ai-mission-key-grant.md (Key-Wrapping-Muster), docs/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)

// 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, 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?