Shared Brain — кросс-проектные знания в Notion
Статус: opt-in, pipeline готов. v1.4 поставляется с интерактивным мастером настройки: чеклист требований сразу при запуске, дружелюбные сообщения при отсутствующем токене, конкретные подсказки про URL и page ID. Запуск bootstrap с --interactive --init сам предложит мастер; иначе — .tausik/tausik brain init.
Локальная проектная память TAUSIK (.tausik/tausik.db) — основной store для всего, что относится к этому репозиторию. Shared Brain — опциональный второй слой: база знаний в Notion, куда пишутся только обобщаемые между проектами знания — дорого добытые архитектурные инсайты, жёсткие подводные камни, стабильные паттерны, и HTTP-кэш, который полезен всем репозиториям.
Разделение намеренное. Локальная БД хранит проектно-специфичные следы (пути к файлам, имена модулей, слаги клиентов) — всё, что может утечь между несвязанными кодовыми базами. Brain хранит то, что ты хотел бы, чтобы свежий агент в другом репозитории унаследовал.
Философия
| Слой | Store | Scope | Пример |
|---|---|---|---|
| Local | .tausik/tausik.db | Только этот проект | "auth-middleware.py строка 42 логирует PII — фикс в MR-1234" |
| Brain | Notion databases | Кросс-проектное | "SHA256-хэш проекта избегает утечки имён и уникален для N<1000" |
Таксономия артефактов (v1.4): общий словарь artifact / pattern / snippet для MCP brain_store_* и будущих snippet-карт — см. brain-artifact-taxonomy.md (опциональное поле и строгий режим в .tausik/config.json).
Редакция памяти: когда объединять локальные строки памяти, а когда заводить новую запись (ортогонально scrubbing) — Память: merge vs новая запись.
Ничто идентифицирующее проект не должно попасть в brain. Защита:
- Scrubbing linter отклоняет записи с абсолютными путями, kebab-слагами ≥3 частей, командами
.tausik/tausik, internal URLs. - Classifier решает,
localилиbrainу записи; толькоbrain-класс попадает в Notion. - Source Project Hash — каждая запись несёт
SHA256(canonical_name)[:16], так что даже если проектный идентификатор случайно проскочит аудит, читатель Notion не сможет сопоставить хэши с именами проектов без локального реестра.
Архитектура
┌────────────────────┐
│ Notion workspace │
│ (4 databases) │
│ decisions │
│ web_cache │
│ patterns │
│ gotchas │
└─────────┬──────────┘
│ Notion REST API
│ (Bearer + Notion-Version)
┌────────────────▼─────────────────┐
│ scripts/brain_notion_client.py │ stdlib urllib,
│ throttle 350ms, 429/5xx retry │ ноль зависимостей
└────────────────┬─────────────────┘
│
┌────────────┴─────────────┐
│ │
pages_create iter_database_query
(write path) (pull с дельтой)
│ │
│ ▼
│ ┌──────────────────────────┐
│ │ scripts/brain_sync.py │
│ │ map Notion→SQLite rows │
│ │ upsert по page_id │
│ │ продвижение sync_state │
│ └────────────┬─────────────┘
│ │
│ ▼
│ ┌──────────────────────────┐
│ │ ~/.tausik-brain/brain.db │
│ │ brain_schema + FTS5 │
│ │ unicode61 tokenizer │
│ └────────────┬─────────────┘
│ │
│ ▼
│ ┌──────────────────────────┐
│ │ scripts/brain_search.py │
│ │ bm25-ранжированный поиск │
│ └────────────┬─────────────┘
│ │
└────────────┬───────────┘
│
┌────────────▼──────────────┐
│ scripts/brain_config.py │
│ загрузка и валидация │
│ project hash, token env │
└───────────────────────────┘Модули (готовы)
| Файл | Назначение |
|---|---|
| scripts/brain_config.py | Парсинг конфига + валидация; compute_project_hash, get_brain_mirror_path |
| scripts/brain_schema.py | Local SQLite DDL (4 таблицы + FTS5 + триггеры, unicode61 токенизатор) |
| scripts/brain_notion_client.py | Stdlib Notion REST клиент (throttle + retry + pagination iterator) |
| scripts/brain_sync.py | Delta-pull Notion → local; маппинг Notion page JSON → SQLite rows |
| scripts/brain_search.py | Локальный FTS5 поиск с bm25 и SQL snippet() |
| brain-db-schema.md | Design-doc — properties, JSON payload примеры, trade-offs |
Настройка
Предпосылка: Notion workspace, который ты контролируешь.
1. Создать parent page
В сайдбаре Notion создай страницу "TAUSIK Shared Brain" (или любое имя). Wizard создаст под ней 4 базы.
2. Создать integration
- https://www.notion.so/my-integrations → "New integration".
- Имя: "TAUSIK Brain".
- Type: Internal.
- Capabilities: Read, Update, Insert content.
- Скопируй internal integration token (начинается с
ntn_или legacysecret_).
3. Дать integration доступ к parent page
Открой страницу "TAUSIK Shared Brain" → справа вверху ... → "Add connections" → выбери "TAUSIK Brain". Wizard создаёт базы под этой страницей — integration должен её видеть.
4. Сохранить токен (выбери одно — v1.3.2+ поддерживает каскад)
Notion-токен резолвится в таком порядке приоритета:
os.environ[NOTION_TAUSIK_TOKEN]— env var. Высший приоритет. Подходит для CI / shared машин..tausik/.env— project-localKEY=VALUEфайл. Рекомендуется для индивидуальных разработчиков: gitignored, не требует правки shell-rc, переживает ребут.brain.notion_integration_tokenв.tausik/config.json— выдаёт stderr warning ("stored inline; prefer .tausik/.env"). Разрешено, но не рекомендуется.
Рекомендуемый путь — .tausik/.env:
echo "NOTION_TAUSIK_TOKEN=ntn_xxx" >> .tausik/.envЕсли предпочитаешь env-переменную:
# Linux / macOS — для persistence добавь в ~/.bashrc / ~/.zshrc / ~/.profile
export NOTION_TAUSIK_TOKEN='ntn_xxx'# Windows — User-level (переживёт ребут, IDE restart подхватит)
[System.Environment]::SetEnvironmentVariable('NOTION_TAUSIK_TOKEN', 'ntn_xxx', 'User')
# Применить к текущей PowerShell-сессии:
$env:NOTION_TAUSIK_TOKEN = 'ntn_xxx'После установки перезапусти IDE (Claude Code / Cursor / ...), чтобы MCP-subprocess подхватил новый env. С .tausik/.env IDE-restart не нужен — токен читается на каждом brain-вызове.
5. Запустить wizard
Один комплект из 4 баз на workspace, общий для всех проектов. Приватность по проектам обеспечивает колонка
Source Project Hashв каждой строке, а НЕ создание отдельных копий 4 баз для каждого проекта. Wizard это требование защищает (см. "Частые ошибки" ниже).
Первый проект — создаём 4 базы:
.tausik/tausik brain initNon-interactive (для CI / скриптов):
.tausik/tausik brain init \
--parent-page-id 'abc123...' \
--token-env NOTION_TAUSIK_TOKEN \
--project-name my-project \
--yes --non-interactiveParent page ID — 32-символьный hex после notion.so/...- в URL (с дефисами или без). Wizard:
- Pre-flight workspace search (v1.3.3+): ищет канонически-озаглавленные BRAIN-базы (
Brain · Decisions / Patterns / Gotchas / Web Cache), уже расшаренные с интеграцией. Если все 4 найдены — отказывается создавать дубли и направляет к--join-existing. - Четыре раза вызывает
POST /v1/databasesдляdecisions,web_cache,patterns,gotchasсо схемами из brain-db-schema.md. - Атомарно пишет
.tausik/config.jsonсbrain.enabled=true, 4database_ids,notion_integration_token_env, именем проекта (для scrubbing blocklist). - Никогда не сохраняет сам токен в
config.json— только имя env-переменной. Сам токен живёт в.tausik/.env(рекомендуется) или в shell-environment.
Повторный запуск в уже настроенном проекте упадёт с ошибкой; для перезаписи — --force.
Второй / третий / N-ный проект — присоединяемся к существующим базам:
.tausik/tausik brain init --join-existingWizard ищет в Notion-workspace канонические 4 BRAIN-базы (auto-discovery через POST /v1/search) и пишет их ID в .tausik/config.json этого проекта. Новые базы не создаются. Все проекты, указывающие на одни и те же 4 ID, делят один knowledge store; колонка Source Project Hash сохраняет различимость по проектам.
Auto-discovery — два прохода (v1.4-polish):
- Title-match. Базы с точным названием
Brain · Decisions / Web Cache / Patterns / Gotchas(как создаёт wizard) подключаются по имени. - Schema-fallback. Для категорий, не сматченных по title, wizard инспектирует
propertiesкаждой оставшейся видимой базы. База, у которойpropertiesсодержат required-набор для категории (например,decisionsтребуетName,Decision,Rationale,Source Project Hash), подключается независимо от title. Перехватывает базы, переименованные в Notion (UI rename, emoji-префикс, перевод) и базы, созданные вне wizard'а.
Когда auto-discovery возвращает 0 совпадений, но интеграция видит другие базы, ошибка перечислит этих кандидатов — можно либо переименовать их канонически, либо передать ID явно. Когда интеграция не видит ничего, ошибка укажет на share-with-integration шаг.
Если auto-discovery не находит базы (например, интеграция не приглашена на parent page), передай ID явно:
.tausik/tausik brain init --join-existing \
--decisions-id '...' \
--web-cache-id '...' \
--patterns-id '...' \
--gotchas-id '...' \
--token-env NOTION_TAUSIK_TOKEN \
--non-interactive --yesАварийный выход — --force-create. Если действительно нужен новый отдельный комплект из 4 баз (другой Notion-account, осознанно изолированное знание), передай --force-create. Wizard пропустит pre-flight проверку и создаст новые. Используй с осторожностью — проекты, указывающие на старый комплект, не будут видеть записи нового, и наоборот.
Частые ошибки
- ❌ Запустить обычный
brain initво втором проекте, использующем тот же workspace → создаст параллельный набор из 4 BRAIN-баз. Знание тихо разделится надвое; одни проекты увидят одну половину, другие — другую. v1.3.3 pre-flight check отказывается это делать по умолчанию — не обходи через--force-create, если только не нужны действительно два независимых "мозга". - ❌ "Отдельные копии для приватности" → не нужно. Используй колонку
Source Project Hash(она уже есть на каждой строке) для фильтрации по проектам; 4 базы делятся между проектами. - ❌ Ручная правка
database_idsвconfig.json→ используй--join-existing --decisions-id ..., чтобы wizard верифицировал каждый ID через Notion перед сохранением.
6. Smoke-тест
from brain_config import load_brain, validate_brain, get_brain_mirror_path
from brain_notion_client import NotionClient
from brain_sync import open_brain_db, sync_all
import os
brain = load_brain()
errors = validate_brain()
assert not errors, errors
client = NotionClient(os.environ["NOTION_TAUSIK_TOKEN"])
conn = open_brain_db(get_brain_mirror_path())
result = sync_all(client, conn, brain["database_ids"])
print(result)get_brain_mirror_path() принимает три формы входа: None (читает load_config() сам), top-level dict {"brain": {...}}, либо уже merged brain dict {"enabled": ..., "local_mirror_path": ...} (то что возвращает load_brain()). Все три разрешаются в один абсолютный путь.
Ожидание: 4 ключа (decisions/web_cache/patterns/gotchas), каждый — {fetched: N, upserted: N, last_edited_time: ...} или {error: ...}. На свежем пустом setup все четыре — {fetched: 0, upserted: 0, last_edited_time: null}.
Метрики (v1.4)
Чтобы ответить на главный вопрос «реально ли brain помогает?», v1.4 пишет каждую brain-операцию в проектную таблицу brain_events (живёт в .tausik/tausik.db, не в зеркале Notion — это сохраняет dispersion firewall между проектами). tausik metrics показывает блок Shared Brain, как только в таблице появляются события:
--- Shared Brain (v1.4) ---
Session: 6 searches, 4 hits, 2 writes, 0 ignored (hit rate: 66.7%)
All-time: 142 searches, 87 hits, 18 writes (hit rate: 61.3%)Счётчики:
searches— каждый вызовbrain_search/search_with_fallback.hits— запросы, вернувшие ≥1 результат (прокси для «brain ответил на вопрос?»).writes— успешныеtry_brain_write_decision/try_brain_write_web_cache(Notion подтвердил). Падения не считаются.ignored— записиtausik_memory_quick brain.ignored:<id>, когда агент пометил подсказку как нерелевантную (в следующей сессии её не покажут снова).
hit_rate_pct = hits / searches * 100. Если в сессии стабильно <20% — значит либо (а) brain пустой/устаревший и нужен tausik brain sync + новые записи, либо (б) запросы слишком project-specific (classifier должен отправлять их в локальную память, не в brain). По метрикам эти две ситуации не различить — читайте последние строки brain_events напрямую.
Телеметрия никогда не блокирует операцию: если INSERT в brain_events упал (заблокированная БД, права), search/write всё равно проходит, а строка просто теряется.
Приватность
- Plaintext-имя проекта никогда не уходит с машины. Единственный per-project идентификатор в brain —
SHA256(canonical_name)[:16]. Canonical name берётся изproject_names[0]локального.tausik/config.jsonи сам никуда не отсылается. - Scrubbing linter (задача
brain-scrubbing, в планах) будет перехватывать каждую запись до клиента. Отклоняет: абсолютные Windows/POSIX пути, internal-domain URLs, любой текст, совпавший с regex-спискомbrain.private_url_patterns, kebab-слаги похожие на internal-идентификаторы. - Classifier (задача
brain-classifier, в планах) выбираетlocalvsbrainпо записи. Толькоbrain-класс пушится. Консервативный default: неоднозначное →local. - Можно отозвать в любой момент. Отозвать integration в Notion или убрать
NOTION_TAUSIK_TOKEN; следующий sync/write падает сNotionAuthError, а локальное зеркало продолжает работать для read-only поиска.
Edge cases / failure modes
| Сценарий | Что происходит | Что делать |
|---|---|---|
| Отозвали integration token | Следующий API-вызов бросает NotionAuthError (401/403) без retry | Восстановить токен; данные не теряются — local mirror цел |
| Rate-limit 429 | Клиент ретраит с учётом Retry-After; исчерпано → NotionRateLimitError | Обычно автоматом. Если упорно: снизить частоту sync |
| Offline / DNS fail | URLError ретраится backoff-ом; исчерпано → NotionError | brain_search.search_local() работает по локальному зеркалу |
| Content >180 KB | В property [see page body]; полный текст в child blocks | Держи заметки компактными; большие web-страницы труncate-ятся |
| Чувствительные данные проскочили scrubbing | Удалить Notion page руками; следующий pull заметит 404 | Улучшить private_url_patterns regex |
| Schema drift баз | Отсутствующие properties читаются как NULL; лишние игнорируются | Добавить недостающие properties (шаг 2) |
| Два проекта с одинаковым canonical name | Коллизия хэшей — записи мешаются в Notion views | Переименовать один в project_names[] |
Плюсы / минусы
| Плюсы | Минусы |
|---|---|
| Пиши один раз — ищи по всем проектам | Нужен Notion-аккаунт + setup |
| Notion UI для просмотра/правки | Rate limits (3 req/s) на bulk writes |
| bm25 local search работает offline | Надо управлять integration-токеном |
| Ноль внешних Python-зависимостей (stdlib urllib) | Нужно активно фильтровать что "обобщаемое" |
| Privacy-preserving hash, ноль plaintext имён | Scrubbing-линтер фильтрует утечки (scripts/brain_scrubbing.py) |
| FTS5 поддерживает кириллицу / диакритику | Нет shared-team режима (single-user в v1) |
Что входит в v1.4
Полный brain-стек ушёл в v1.4 — read И write пути, MCP-инструменты с обеих сторон, hook-driven захват WebFetch, classifier + scrubbing, init-wizard, offline fallback, search-proactive хук. Модули: brain_mcp_read.py, brain_mcp_write.py, brain_classifier.py, brain_scrubbing.py, brain_search.py (bm25 ранжирование со stack boost), brain_init.py (init wizard), brain_project_registry.py, brain_fallback.py, brain_universality.py + brain_universality_semantic.py (эвристики кросс-проектной универсализации). Типизированная иерархия ошибок: NotionAuthError, NotionNotFoundError, NotionRateLimitError, NotionServerError. Ручные one-time шаги (brain-notion-space, brain-integration-token) задокументированы в выводе tausik brain init.