Status: P0–P4 + sync mode + Realtime full push (#31) + semantic recall opt-in (#4) shipped (anon rate limits closed — see #32)
Related: #2, team-brain.md, team-brain-onboarding.md, scopes.md
This document captures the original Team Brain intent, what shipped for collaborative AI memory (FTS + optional semantic recall + agent loop), and remaining gaps — without depending on external memory products.
When engineer-brain was personal-only, Team Brain meant:
| Intent | Detail |
|---|---|
| Problem | 2–3 engineers on the same spike each cold-start AI → duplicate research and token burn |
| Product | Initiative-scoped shared context (research, decisions, open questions) |
| Privacy | Personal BRAIN.md stays private; team layer is opt-in |
| Commands | init / attach / sync / breakdown (+ team config) |
| Portability | Same cross-platform story as personal brain |
| Success | Less duplicate research, lower tokens before first PR, faster 2nd/3rd engineer onboard, better epic breakdown |
Markdown was always a portable surface. The outcome was agents reusing team memory — not a shared wiki.
capture + pull sync → rewrite ## Capture log in initiatives/<KEY>.mdThat thin log is kept as compat aliases. Current product: remember / recall → Supabase SoT + cache/<KEY>.json, MCP, Cursor agent loop, breakdown / metrics.
Not a notebook. Collaborative AI memory between engineers on a Jira key, backed only by Supabase.
Engineer A agent Supabase Engineer B agent
| | |
|-- remember(jira, kind, body) -->| INSERT memory (+ embed async) |
| |-- Realtime INSERT event ---------->|
| | |-- inject into context
|-- recall(jira, "auth flow") --->| vector / FTS search |
|<-- top-k memories --------------| |
| Layer | Role |
|---|---|
| Jira | Initiative spine (YOU_JIRA_TICKET_HERE) |
| Supabase | Source of truth for memories + membership + realtime |
| Local cache | .team-brain/cache/<KEY>.json for agents (fast read) |
| Markdown export | Optional human/git mirror — not the sync bus |
| Mode | Trigger |
|---|---|
| Realtime (preferred) | Subscribe to inserts for attached initiatives |
| Periodic / session | On attach, skill start, before breakdown, or every N minutes |
| Write | remember when a finding is durable (agent or human) |
Re-running sync must not duplicate rows. Local cache/log is rebuilt or merged from server identity (id / source_ref).
The product must deliver this core memory loop:
rememberrecall / search_memories (FTS → optional pgvector)watch today; push later)remember, recall, list_recentsource_ref (+ content hash) unique per initiativeProduct fit (Brain umbrella):
team-brain-bootstrap.sh / bootstrap) for migrate + register + share bundle (#41)recall before research, remember after findingscorrect / same source_ref update + optional learningbreakdown consumes recallLater polish: version/rollback, snapshots, richer metrics.
source_ref, content_hash, FTS column; soft dedup on remembersupabase/migrations/20260728000001_team_brain_memory.sql — apply on live project)remember, search_memories, list_recent (+ add_capture wrapper)remember / recall; attach pulls recent → cache/<KEY>.jsoncapture / sync as compatibility aliasesDecision (poll): use authenticated polling (list_recent + p_since), not postgres_changes.
Reason: captures revoke SELECT from anon; auth is custom p_api_key on RPCs. Realtime CDC uses the JWT role and cannot see rows without opening SELECT to everyone (leak). Polling keeps member-key security for content.
Decision (push, #31 — full content): rather than migrating membership to Supabase Auth JWTs to unlock private Realtime Authorization, Team Brain closes the same gap with application-layer encryption. Each team gets a random 256-bit broadcast_key (server-generated; returned only to a resolved member via memory_broadcast_topic). The CLI encrypts the body (AES-256-CBC + HMAC-SHA256, encrypt-then-MAC) before remember, and the DB trigger forwards that opaque body_ct — still on the same public topic team-brain:{team_id}:{JIRA_KEY} — never decrypting it. A peer holding the key decrypts inline in team-brain-realtime.py and writes straight into .team-brain/cache/<KEY>.json: zero extra RPC round-trip. Anyone without the key (or without the optional cryptography dependency) only ever sees ciphertext, and transparently falls back to the original signal + authenticated _pull_signal pull — the same safety net #31 shipped with initially.
team-brain-api.sh watch <JIRA-KEY> [secs] — poll, print deltas, refresh cache/md20260728000002_team_brain_watch_notes.sql20260804000001_team_brain_realtime_broadcast.sql + team-brain-realtime.py20260808000001_team_brain_full_push_and_semantic_hardening.sql — encrypted body_ct, per-team broadcast_key, inline decrypt-and-cache in the listenerwatch / sync-mode loop if Realtime, websockets, or cryptography unavailable (TEAM_BRAIN_REALTIME=off); also falls back on HMAC mismatch, decrypt failure, or a restored row (ciphertext cleared server-side to force a correct re-pull)watch in background or periodic recall (#37)SELECT — the DB never decrypts body_ct; it is opaque end-to-end except to members who resolved the key with a valid api_keyembedding vector(768) + pgvector (20260729000001_team_brain_embeddings.sql)TEAM_BRAIN_EMBED_PROVIDER is set (OpenAI or Ollama)search_memories cosine when query embedding provided; else FTSreembed <KEY> backfill via set_memory_embeddingenable-semantic <openai|ollama> persists provider/model to team.yaml (non-secret, shareable — every teammate who pulls the repo/config inherits it) and tests one live embed call| Provider | Env | Notes |
|---|---|---|
| none (default) | unset | FTS-only recall; no API key needed |
openai |
TEAM_BRAIN_EMBED_PROVIDER=openai + TEAM_BRAIN_EMBED_API_KEY or OPENAI_API_KEY |
Model default text-embedding-3-small with dimensions=768. Cost: ~$0.02 per 1M input tokens (Aug 2026 pricing) — a crew writing a few hundred memories/month costs cents. Body leaves your machine → OpenAI. |
ollama |
TEAM_BRAIN_EMBED_PROVIDER=ollama |
Default model nomic-embed-text (768-d); TEAM_BRAIN_EMBED_BASE_URL optional. Cost: $0 (local inference). Body never leaves your machine — best choice for sensitive/regulated content. |
One-command opt-in (recommended path):
bash core/scripts/team-brain-api.sh enable-semantic openai # or: enable-semantic ollama
export TEAM_BRAIN_EMBED_API_KEY=sk-... # openai only — never persisted to team.yaml
bash core/scripts/team-brain-api.sh enable-semantic openai # re-run to verify: prints vector dims on success
bash core/scripts/team-brain-api.sh remember YOU_JIRA_TICKET_HERE research "EE schema path lives in …"
bash core/scripts/team-brain-api.sh recall YOU_JIRA_TICKET_HERE "where is decision_environment scaffolded"
# → stderr: "recall mode: vector (openai)"; response body: "mode": "vector"
bash core/scripts/team-brain-api.sh reembed YOU_JIRA_TICKET_HERE # backfill memories written before opt-in
Manual/legacy path (still works, e.g. for one-off scripting): export TEAM_BRAIN_EMBED_PROVIDER / _MODEL / _BASE_URL directly — enable-semantic is just a documented, persisted shortcut for the same env vars.
When FTS is enough vs when to turn on vectors: FTS (default, zero setup) is fine for exact/near-exact keyword recall — “find the memory where we discussed X”. Turn on vectors once a crew is asking conceptual questions (“did anyone already figure out auth for this service?”) where the right memory doesn’t share vocabulary with the query.
remember, recall, list_recent, attach, whoami, … (mcp/team-brain/)source_ref / content-hash dedup (P0 RPCs; documented on MCP remember)platforms/cursor/rules/team-brain.mdc + skill + MCP instructions)correct CLI/MCP; learning kind; skill + onboarding20260802000001_team_brain_learning_kind.sql, issue #30)capture_revisions; archive on source_ref update;history / restore CLI+MCP (20260803000001_team_brain_memory_history.sql, issue #34)delete_memory RPC; memory_deletions audit; read RPCs exclude tombstones;deleted: true + authoritative poll replace (20260908000001_team_brain_delete_permissions.sql, issue #66)notify/<KEY>.json (#31); poll remains fallbackEngineer A laptop Supabase Engineer B laptop
| | |
|-- recall(KEY) ----------------------->| |
|<-- crew memories ---------------------| |
|-- (research) --- remember(KEY, ref) ->| |
| |<-- recall(KEY) --------------------|
| |--- memories (incl. A's) ---------->|
| | |-- (reuse, then research)
Humans can still run CLI manually; agents must not skip the loop.
breakdown consumes recall → initiatives/<KEY>-breakdown.md (CLI + MCP).team-brain/metrics.json + metrics commandmetrics --team / aggregate (collab graph deferred)bash core/scripts/team-brain-api.sh breakdown YOU_JIRA_TICKET_HERE
bash core/scripts/team-brain-api.sh metrics YOU_JIRA_TICKET_HERE
# → initiatives/YOU_JIRA_TICKET_HERE-breakdown.md + metrics.json (gitignored)
bash core/scripts/team-brain-api.sh metrics --team # crew coverage + reuse (#35)
| In scope (opt-in Team Brain activity) | Out of scope for v1 |
|---|---|
Member display_name × memory kind counts |
Personal BRAIN.md / career skills |
Member × initiative (jira_key) memory counts |
Memory bodies / source_ref text in aggregate payload |
| Memories per initiative + ISO-week activity | GitHub/GitLab review collaboration graph |
Local metrics.json recall overlay (this machine only) |
Uploading local metrics to the server |
Crew members with a valid team api_key |
Org-wide dashboards beyond the crew |
CLI: metrics --team or aggregate → prefers RPC team_aggregate_metrics (migration 20260807000001_…); falls back to list_recent with bodies stripped.
Privacy: same boundary as list_initiatives — never uploads personal BRAIN.md; aggregate response has no bodies.
Parent: #2 · issue #35 · roadmap: roadmap.md
Use this as the build board (check off in PRs):
| ID | Phase | Task | Owner notes |
|---|---|---|---|
| M1 | P0 | Migration …_team_brain_memory.sql |
Apply after v1 (SQL Editor) |
| M2 | P0 | remember RPC (+ dedup) |
✅ in migration |
| M3 | P0 | search_memories + list_recent |
✅ FTS |
| M4 | P0 | CLI remember / recall + cache write |
✅ |
| M5 | P0 | Skill/docs language shift | ✅ |
| M6 | P1 | watch (authenticated poll) |
✅ |
| M7 | P2 | pgvector + embed path | ✅ — apply …embeddings.sql |
| M8 | P3 | MCP server tools | ✅ mcp/team-brain/ |
| M9 | P4 | breakdown ← recall + metrics | ✅ |
BRAIN.mdcredentials.json or service_role(team_id, display_name); invite codes 16 hex charsteam_aggregate_metrics returns counts + display_name only — never memory bodies, never personal BRAIN.md, never local metrics.json uploadregister_team / join_team are DB-level rate limited (sliding window; default 5 register/h, 15 join/h) — see supabase/README.mdwatch) + full-content Broadcast — body travels app-layer encrypted (body_ct), never plaintext, never via a widened anon SELECT; the DB stores/forwards ciphertext but never decrypts itremember rejects bodies over 20,000 characters (hardening — see migration 20260808000001_…)doctor gives a client-side readiness preflight (deps, config, migrations, push/embedding mode) — run it before reporting “Team Brain is broken”onboard + auto-recall)When poisoned or stale team context must be removed (not just corrected), members and admins call delete_memory — viewers cannot.
| Layer | Behavior |
|---|---|
| Server | Sets captures.deleted_at; appends memory_deletions + capture_revisions audit rows |
| Read RPCs | list_recent, search_memories, team_aggregate_metrics exclude tombstones |
| Undelete | remember at the same source_ref clears the tombstone (undeleted: true) |
| Local cache | Authoritative poll replace each sync cycle; realtime push sends deleted: true so peers purge without manual cache wipe |
# Tombstone (member/admin)
bash core/scripts/team-brain-api.sh delete YOU_JIRA_TICKET_HERE --source-ref "YOU_JIRA_TICKET_HERE#bad-claim"
# Admin audit crew tiers
bash core/scripts/team-brain-api.sh list-members
Workshop Part 5 flow: A stores poison → B recalls → B deletes → A’s cache evicts on next poll or realtime tombstone signal → A no longer recalls the deleted finding.
Smoke tests: tests/team-brain/cache-purge-smoke.sh (local), tests/team-brain/governance-smoke.sh (live RPC, optional env).
Feedback engine: remember detects near-duplicates (FTS; vector when embeddings enabled) and cross-author source_ref overrides on research/decision. Returns redundant_candidate: true — nothing is stored until the author fixes the slug or queues for review.
| Path | Trigger | Notes |
|---|---|---|
| Vector (when embedding supplied) | cosine distance ≤ 0.12 (~similarity ≥ 0.88) | Issue #67 draft suggested 0.08 (≥0.92); v1 uses 0.12 to reduce false blocks on paraphrases |
| FTS (fallback / no embedding) | ts_rank ≥ 0.05, query body ≥ 8 chars |
Uses plainto_tsquery('english', …) on search_tsv |
| Exact hash | identical content_hash |
Auto-dedupe (unchanged) |
Cross-author source_ref |
research/decision only |
note/learning still auto-update same source_ref (author merge) |
| Outcome | Key fields |
|---|---|
Success (inserted / updated / deduped) |
id, initiative_id, content_hash, has_embedding, author_member_id, author_name, created_at, updated_at, plus result |
| Blocked | redundant_candidate: true, matches[], conflict_reason, suggested_action |
| Queued | pending_submitted: true, pending_id, conflict_reason |
suggested_action values (v1): recall_and_merge_same_source_ref_or_queue_for_review, use_same_source_ref_after_recall_or_queue_with_p_queue_for_review, await_admin_approval.
Admin approve: promotes queued body to live memory and attributes author_member_id to the submitter (credit for the improved finding).
| Situation | Default | Member escape hatch | Admin |
|---|---|---|---|
| Identical body | Auto-dedupe | — | — |
Same source_ref, same author |
Auto-update + archive | — | --force immediate |
Same source_ref, different author (research/decision) |
Block → redundant_candidate |
remember … --queue |
pending approve <pending-id> |
| New body, semantic/FTS match | Block → redundant_candidate + matches[] |
remember … --queue |
pending approve <pending-id> |
# Member blocked — see matches, reuse source_ref:
bash core/scripts/team-brain-api.sh remember DEMO-1 research "…"
# → redundant_candidate (not stored)
# Member queues override for admin:
bash core/scripts/team-brain-api.sh remember DEMO-1 research --source-ref "DEMO-1#auth" --queue "…"
# Admin reviews + approves (promotes to live memory):
bash core/scripts/team-brain-api.sh pending list DEMO-1
bash core/scripts/team-brain-api.sh pending approve <pending-id> --note "Better finding"
# Admin rejects (keeps existing live memory):
bash core/scripts/team-brain-api.sh pending reject <pending-id> --note "Duplicate of Alice's note"
Workshop Part 5: B tries to re-store A’s topic → feedback engine blocks → B recalls A’s source_ref OR queues → admin keeps only the improved version.
Admin review dashboard: dashboard/ — local /admin/review UI (same RPCs as CLI). Run npm run dev in dashboard/, open /admin/review, connect with admin tb_… key. GitHub Pages demo does not include live review (credentials stay local).
| Capability | Today | Next |
|---|---|---|
| Recall before research | ✅ Rule/skill/MCP | Keep mandatory |
| Remember after findings | ✅ Direct save | Keep mandatory |
Dedup (source_ref) |
✅ | — |
| Redundant guard + pending queue | ✅ CLI/MCP (#67) | ✅ Admin dashboard UI (dashboard/admin/review) |
| Correction / learning | ✅ correct + learning kind |
— |
| Version/history/soft rollback | ✅ history / restore + capture_revisions |
Optional snapshots / UI |
| Tombstone delete / governance | ✅ delete + list-members; peer cache eviction |
Per-initiative ACLs (deferred) |
| Semantic search | ✅ One-command opt-in (enable-semantic); FTS default (#4) |
— |
| Live push into other agent context | ✅ Full-content encrypted Broadcast + poll fallback (#31) | Optional key rotation command if a member is offboarded |
| Model compliance | ✅ Stronger prompts + soft session gate (compliance / prepare_research; CLI not hard-blocked) |
Optional hard gate / metrics later |
| Metrics | Local metrics.json + crew metrics --team (#35) |
Optional team dashboard UI |
| Request/body hardening | ✅ remember 20000-char body cap; curl --max-time on all RPCs; doctor readiness preflight |
— |