Merge remote-tracking branch 'origin/main'
This commit is contained in:
Executable
+87
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: Media Pipeline Pitfalls
|
||||
created: '2026-05-23'
|
||||
updated: '2026-05-27'
|
||||
type: tech
|
||||
namespace: family
|
||||
tags: [media-pipeline, pitfalls, torrents, infra]
|
||||
related:
|
||||
- "[[tech/arr-stack-kraken]]"
|
||||
- "[[concepts/watchlist-automation]]"
|
||||
---
|
||||
|
||||
# Media Pipeline Pitfalls
|
||||
|
||||
## Torrents — Do Not Rename Files
|
||||
|
||||
Never rename torrent source files or folders while a torrent is active.
|
||||
Transmission tracks files by path. Renaming breaks the association and
|
||||
causes re-download or stalled seeding.
|
||||
|
||||
Правильный порядок (когда файл уже лежит в нужном месте, но с другим кейсом):
|
||||
1. Переименовать файл/папку на диске (Linux ext4 case-sensitive)
|
||||
2. Вызвать `torrent-set-location` с новым путём + `move=false`
|
||||
3. Запустить торрент на сидирование
|
||||
|
||||
## torrent-set-location Updates downloadDir
|
||||
|
||||
`torrent-set-location` in the Transmission RPC updates `downloadDir` in
|
||||
the torrent metadata. Use this when moving completed files, not a manual
|
||||
rename. Sequence:
|
||||
|
||||
1. Move files to new location on disk
|
||||
2. Call `torrent-set-location` with the new path + `move=false`
|
||||
3. Verify torrent goes back to seeding state
|
||||
|
||||
## Router Creates Subdirectories — Transmission downloadDir Stale
|
||||
|
||||
`router.py` creates subdirectory per title under the genre folder
|
||||
(lowercase name, e.g. `/media/cartoons/бобик в гостях у барбоса/`).
|
||||
Transmission still holds the old `downloadDir` pointing to the parent
|
||||
(`/media/cartoons/`). Result: torrents show "No data found" error.
|
||||
|
||||
Fix:
|
||||
1. Find the actual subdirectory on disk
|
||||
2. Call `torrent-set-location` with the subdirectory path + `move=false`
|
||||
3. Transmission verifies and resumes seeding
|
||||
|
||||
## Alpine Container — No Docker-in-Docker
|
||||
|
||||
`on-download-complete.sh` runs inside the Transmission Alpine container.
|
||||
Docker CLI is not available there — using `docker run` inside the container
|
||||
fails silently or errors. Python 3.14 IS available in the Alpine image.
|
||||
|
||||
Solution: `sync.py` (Python3, ~190 lines) in the same container:
|
||||
- Reads `config.json` for sorting rules + credentials (same file as linker container)
|
||||
- Queries TMDB for genre routing
|
||||
- Creates hardlinks + calls Transmission RPC `torrent-set-location`
|
||||
Location: `/srv/.../docker/media-pipeline/sync.py`
|
||||
|
||||
## Transmission download-dir — без /complete
|
||||
|
||||
download-dir: `/downloads` (не `/downloads/complete`).
|
||||
incomplete-dir: `/downloads/incomplete` (enabled).
|
||||
|
||||
Менять через остановку Transmission → правка `settings.json` → запуск.
|
||||
|
||||
## media-router.sh — Telegram-уведомления
|
||||
|
||||
Скрипт-обёртка над `router.py`. Запускается из crontab каждые 10 минут.
|
||||
- При `Nothing to do` — SILENT
|
||||
- При активности — отправляет отчёт в Telegram Kraken (kraken_htpc_bot)
|
||||
- `BOT_TOKEN` и `CHAT_ID` хардкодом в скрипте
|
||||
|
||||
## Удаление дубликатов из downloads/complete
|
||||
|
||||
После перемещения файлов в `/media/{movies,cartoons,series,...}/`:
|
||||
1. Проверить, что файлы в movies — **реальные** (-rwx), не symlink'и
|
||||
2. Проверить что inode разные (это копии, не один файл)
|
||||
3. Удалить из `downloads/complete/`
|
||||
4. Обновить путь в Transmission
|
||||
|
||||
## General Rules
|
||||
|
||||
- Never touch source files/folders (media-pipeline USER.md rule)
|
||||
- `resolve-manual` is emergency-only
|
||||
- NFO files: do not create manually; let the pipeline handle them
|
||||
- Never delete test files from the pipeline project
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
namespace: work
|
||||
tags: [system, agent, memory, architecture, ai]
|
||||
created: '2026-05-18'
|
||||
updated: '2026-05-18'
|
||||
last_synced: '2026-05-18'
|
||||
confidence: 0.85
|
||||
sources:
|
||||
- arXiv 2603.07670 (Memory for Autonomous LLM Agents, март 2026)
|
||||
- zylos.ai AI Agent Memory Architectures survey (апрель 2026)
|
||||
- wiki/raw/inbox/research-vault-strategy-memory-20260516.md
|
||||
---
|
||||
# Agent Memory Architecture
|
||||
|
||||
Когнитивная таксономия памяти для LLM-агентов, основанная на arXiv 2603.07670. Применительно к personal-os (Hermes).
|
||||
|
||||
## Три уровня памяти
|
||||
|
||||
| Уровень | Определение | Реализация в Hermes |
|
||||
|---------|-------------|----------------------|
|
||||
| **Episodic** | Сессионные логи — что произошло в конкретных взаимодействиях | SQLite + `session_search` ✅ |
|
||||
| **Semantic** | Факты, предпочтения, постоянное знание | `MEMORY.md` + `USER.md` (нужна curation) |
|
||||
| **Procedural** | Навыки, workflows, пошаговые паттерны | `skills/` ✅ |
|
||||
|
||||
## Ключевые выводы из arXiv 2603.07670
|
||||
|
||||
- **Gap между "есть память" и "нет памяти"** > gap между разными LLM backbone. Выбор памяти важнее выбора модели.
|
||||
- **Без рефлексии агент деградирует:** в Generative Agents эксперименте — 48 ч без memory reflection → repetitive behavior.
|
||||
- **Summarization drift:** после 3+ циклов сжатия критические инструкции теряются. Решение: vault как immutable store, не только MEMORY.md.
|
||||
|
||||
## Overflow Rule (MEMORY.md)
|
||||
|
||||
Запись в MEMORY.md — только короткие стабильные факты:
|
||||
- Длина > 150 символов → перенести в vault, оставить pointer
|
||||
- Содержит пошаговые инструкции → в `skills/`
|
||||
- Детальный технический контекст → в `wiki/tech/`
|
||||
|
||||
```
|
||||
MEMORY.md: "media-pipeline питфолы → wiki/tech/media-pipeline-pitfalls.md"
|
||||
wiki/tech/media-pipeline-pitfalls.md: (полный разбор)
|
||||
```
|
||||
|
||||
## 4-tier pipeline (LLM Wiki v2 / rohitg00)
|
||||
|
||||
Расширенная модель:
|
||||
```
|
||||
Working Memory → сырые наблюдения текущей сессии
|
||||
Episodic Memory → сжатые саммари сессий
|
||||
Semantic Memory → кросс-сессионные факты (wiki)
|
||||
Procedural Memory → паттерны, workflows, skills
|
||||
```
|
||||
|
||||
**Confidence scoring:** каждый факт имеет score (кол-во источников, свежесть, противоречия). Устаревает со временем.
|
||||
|
||||
**Event-driven automation:**
|
||||
- New source → auto-ingest
|
||||
- Session end → compress into observations
|
||||
- On query → check if answer worth filing back
|
||||
- On schedule → periodic lint
|
||||
|
||||
## Правило разграничения (Alex's personal-os)
|
||||
|
||||
```
|
||||
MEMORY.md → SSH хосты, namespace rules, токены, конфиги (≤150 chars)
|
||||
USER.md → предпочтения, стиль (редко меняется)
|
||||
wiki/tech/ → технические питфолы, баг-паттерны (длинные, с контекстом)
|
||||
personal/projects/ → статус проектов, дебаггинг-логи
|
||||
skills/ → процедуры и workflows пошагово
|
||||
```
|
||||
|
||||
## Связанные страницы
|
||||
|
||||
- [[personal-os-architecture]] — как память встроена в общий стек
|
||||
- [[wiki-ingest-process]] — как сессии кристаллизуются в wiki
|
||||
- [[personal-os-self-modification]] — как система эволюционирует
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
---
|
||||
title: Autonomous Agent Safety Patterns
|
||||
created: '2026-05-24'
|
||||
updated: '2026-05-24'
|
||||
type: concept
|
||||
tags: [agent, security, architecture, executor, rules]
|
||||
sources:
|
||||
- wiki/concepts/executor-security-incident.md
|
||||
- wiki/concepts/executor-orchestrator.md
|
||||
- wiki/personal-os-agent-rules.md
|
||||
confidence: high
|
||||
related:
|
||||
- "[[concepts/executor-security-incident]]"
|
||||
- "[[concepts/executor-orchestrator]]"
|
||||
- "[[personal-os-agent-rules]]"
|
||||
---
|
||||
|
||||
# Autonomous Agent Safety Patterns
|
||||
|
||||
Design principles for autonomous LLM agents distilled from the 2026-05-11
|
||||
executor security incident. General enough to apply beyond the personal-os context.
|
||||
|
||||
## The Three Failure Modes (from incident)
|
||||
|
||||
### 1. Mandatory prompt steps that outrank modes
|
||||
|
||||
The executor wrote Asana comments during "recording-only" mode because the
|
||||
worker prompt declared comment posting a *mandatory completion action* — not
|
||||
subject to mode flags.
|
||||
|
||||
**Pattern:** Every completion action (write to external system, post comment,
|
||||
send notification) must be guarded by a mode check that the agent cannot
|
||||
override.
|
||||
|
||||
```
|
||||
IF mode == "recording-only":
|
||||
SKIP external writes
|
||||
LOG "would have posted: ..." instead
|
||||
```
|
||||
|
||||
### 2. Boundary policies that only cover exfiltration
|
||||
|
||||
The lethal-trifecta policy blocked HTTP to attacker domains after internal
|
||||
MCP access. It did NOT block writes *to* internal systems (Asana).
|
||||
|
||||
**Pattern:** Separate the threat models:
|
||||
- **Exfiltration** = data leaving to unauthorized destinations → block outbound
|
||||
- **Unauthorized writes** = data going to authorized systems without approval → require
|
||||
explicit confirmation gate per write type
|
||||
|
||||
These are different controls. A policy that only covers one leaves the other open.
|
||||
|
||||
### 3. High-level directives not propagated to sub-prompts
|
||||
|
||||
"Don't touch anything" was a session-level directive. The worker
|
||||
sub-prompt (spawned per task) didn't inherit it — it ran its own
|
||||
completion protocol.
|
||||
|
||||
**Pattern:** Mode flags must be passed explicitly to every spawned
|
||||
sub-process/sub-prompt as a first-class parameter, not assumed from
|
||||
session context.
|
||||
|
||||
## The Auto-Approve Table Pattern
|
||||
|
||||
From [[concepts/executor-orchestrator]]: instead of blanket trust or blanket
|
||||
denial, classify actions by risk tier:
|
||||
|
||||
| Risk | Action type | Default |
|
||||
|------|-------------|---------|
|
||||
| Low | git, build, test, worktree | auto-approve |
|
||||
| Medium | draft PR, push branch | auto-approve with log |
|
||||
| High | post Asana comment, merge PR | require Alex confirmation |
|
||||
| Blocked | autonomous Asana write | denied always |
|
||||
|
||||
This table lives in the orchestrator, not the worker. Workers *request*
|
||||
actions; orchestrator decides.
|
||||
|
||||
## Least-Privilege Credential Design
|
||||
|
||||
From incident: `ASANA_API_KEY` was full-account CRUD (PATs are not granular).
|
||||
One compromised agent → full Asana write access.
|
||||
|
||||
**Pattern:** Scope credentials to the minimum required operation:
|
||||
- Read-only keys for read-only agents
|
||||
- Write keys injected only at the moment of approved write
|
||||
- Never persist write credentials in always-on agent environments
|
||||
|
||||
## Audit Before Autonomous
|
||||
|
||||
The incident ran 18 PRs and 5 Asana comments before detection. Detection only
|
||||
happened because Alex checked manually.
|
||||
|
||||
**Pattern:** Autonomous runs should produce an observable audit trail that
|
||||
can be reviewed without running the agent:
|
||||
- Structured log per run (not just stdout)
|
||||
- Diff-friendly format (what was written, to where, at what time)
|
||||
- Periodic summary posted to a channel Alex monitors
|
||||
|
||||
## Summary: Checklist for New Autonomous Agents
|
||||
|
||||
- [ ] Every external write is behind a mode-guard (can "recording-only" block it?)
|
||||
- [ ] Exfiltration and unauthorized-write policies are separate controls
|
||||
- [ ] Mode flags propagate explicitly to sub-prompts
|
||||
- [ ] Auto-approve table is in the orchestrator, not the worker
|
||||
- [ ] Credentials are scoped to minimum; write keys not always-on
|
||||
- [ ] Each run produces a structured audit log
|
||||
- [ ] Audit log goes somewhere Alex sees without hunting
|
||||
|
||||
## See Also
|
||||
|
||||
- [[concepts/executor-security-incident]] — incident post-mortem with full timeline
|
||||
- [[concepts/executor-orchestrator]] — post-incident architecture (orchestrator pattern)
|
||||
- [[personal-os-agent-rules]] — Eagle's specific rules derived from these patterns
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
title: Hermes Deployment Patterns
|
||||
created: '2026-05-29'
|
||||
updated: '2026-05-29'
|
||||
type: concept
|
||||
namespace: personal
|
||||
tags: [hermes, architecture, agent, eagle, kraken, infra]
|
||||
sources: []
|
||||
confidence: medium
|
||||
related:
|
||||
- "[[tech/hermes-eagle-mac]]"
|
||||
- "[[tech/hermes-docker-kraken]]"
|
||||
- "[[tech/hermes-kraken-api]]"
|
||||
- "[[personal-os-architecture]]"
|
||||
---
|
||||
|
||||
# Hermes Deployment Patterns
|
||||
|
||||
Three distinct ways Hermes runs in the Personal OS ecosystem. Each serves
|
||||
a different access model and client type.
|
||||
|
||||
## Pattern 1: Eagle Native (Mac M4)
|
||||
|
||||
**Where:** Eagle Mac M4 Max, native process (not Docker)
|
||||
**Transport:** Zulip (Docker)
|
||||
**Model backend:** `openclaw-claude-proxy` on port 3456 — wraps
|
||||
`claude --print` as an OpenAI-compatible endpoint, preserving tool_use.
|
||||
**Autostart:** launchd (`ai.claude-proxy.plist`)
|
||||
|
||||
**Why native:** Mac file system access, MCP tools (obsidian-mcp), and the
|
||||
cron wiki-curation job all need full host access. Docker would require
|
||||
volume mounts for every integration.
|
||||
|
||||
**Key pitfall:** `claude-proxy` wrapper script must `source ~/.hermes/.env`
|
||||
explicitly — launchd does not inherit login session env. See
|
||||
[[tech/hermes-eagle-mac]].
|
||||
|
||||
## Pattern 2: Kraken Docker (RPi5)
|
||||
|
||||
**Where:** Kraken RPi5, `docker run` (not compose)
|
||||
**Transport:** Zulip (same instance or separate)
|
||||
**Model backend:** Gemini or OpenRouter via Hermes gateway
|
||||
**Autostart:** `--restart unless-stopped` on the container
|
||||
|
||||
**Why docker run, not compose:** explicit restart behavior; avoids accidental
|
||||
`docker compose down` wipes. Config volume: `~/.hermes` on Kraken host.
|
||||
|
||||
See [[tech/hermes-docker-kraken]].
|
||||
|
||||
## Pattern 3: Kraken API Server (OpenAI-compat)
|
||||
|
||||
**Where:** Kraken RPi5, same hermes-kraken container
|
||||
**Transport:** HTTPS via Cloudflare Tunnel (`kraken.qentra.top`)
|
||||
**Model backend:** Gemini / OpenRouter (same gateway)
|
||||
**Clients:** Android Aide app (BYOK → Custom Endpoint), any OpenAI SDK
|
||||
|
||||
**Why Cloudflare Tunnel:** no port-forwarding on home router required.
|
||||
The tunnel terminates at cloudflared running with `network_mode: host`,
|
||||
hitting `localhost:8642` directly.
|
||||
|
||||
See [[tech/hermes-kraken-api]].
|
||||
|
||||
## Comparison
|
||||
|
||||
| Dimension | Eagle Native | Kraken Docker | Kraken API |
|
||||
|-----------|-------------|---------------|-----------|
|
||||
| Model | claude-sonnet via proxy | Gemini/ORouter | Gemini/ORouter |
|
||||
| Transport | Zulip | Zulip | HTTPS REST |
|
||||
| Clients | Cron, MCP tools | Zulip bot clients | Mobile / OpenAI SDK |
|
||||
| External access | No | No | Yes (CF Tunnel) |
|
||||
| MCP/tools | Full (host access) | Docker volumes | Not applicable |
|
||||
| Rate limits | Claude OAuth (proxy workaround) | API keys | API keys |
|
||||
|
||||
## Design Principle
|
||||
|
||||
Hermes deployments follow the client's access model:
|
||||
- **Interactive/tool-heavy** → Eagle native (full host, MCP)
|
||||
- **Always-on background** → Kraken Docker (low-power, 24/7)
|
||||
- **Mobile / external** → Kraken API server (HTTPS, standard protocol)
|
||||
|
||||
This avoids running a single large instance with conflicting requirements.
|
||||
See [[personal-os-architecture]] for the full system overview.
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: Hermes — Native (Eagle) vs Docker (Kraken) Deployment
|
||||
created: '2026-05-22'
|
||||
updated: '2026-05-22'
|
||||
last_synced: '2026-05-22'
|
||||
type: comparison
|
||||
namespace: work
|
||||
tags:
|
||||
- hermes
|
||||
- deployment
|
||||
- mac
|
||||
- kraken
|
||||
- comparison
|
||||
confidence: 0.9
|
||||
sources:
|
||||
- wiki/tech/hermes-eagle-mac.md
|
||||
- wiki/tech/hermes-docker-kraken.md
|
||||
---
|
||||
# Hermes — Native (Eagle) vs Docker (Kraken)
|
||||
|
||||
Два способа запуска Hermes Agent: нативно на Mac M4 Max и в Docker на Raspberry Pi 5.
|
||||
|
||||
## Сравнение
|
||||
|
||||
| | Eagle (Mac M4 Max) | Kraken (RPi5) |
|
||||
|--|-------------------|--------------|
|
||||
| Запуск | `hermes gateway` (нативно) | `docker run` (не compose) |
|
||||
| Транспорт | Zulip (в Docker) | Zulip (тот же) |
|
||||
| Модель | openclaw-claude-proxy (`localhost:3456`) | openclaw-claude-proxy (аналогично) |
|
||||
| Процесс-менеджер | launchd (`ai.claude-proxy`) | Docker `--restart=unless-stopped` |
|
||||
| PATH в launchd | нужен явный `export PATH=...` | нет проблемы (Docker env) |
|
||||
| Токен | `source ~/.hermes/.env` в wrapper | передаётся через `-e` флаг docker run |
|
||||
| MCP | mcpvault (`/Users/admin/obsidian`) | mcpvault (`/vault` — mount) |
|
||||
| Логи Claude-proxy | `~/.hermes/logs/claude-proxy.log` | Docker logs |
|
||||
| Навыки | все Hermes skillsets | ограниченный набор (нет macOS tools) |
|
||||
| HERMES_SKIP_CHOWN | не нужно | нужно (`-e HERMES_SKIP_CHOWN=1`) |
|
||||
|
||||
## Pitfalls Eagle (Mac M4 Max)
|
||||
|
||||
**openclaw-claude-proxy через launchd:**
|
||||
1. launchd не наследует login-сессию → CLAUDE_CODE_OAUTH_TOKEN не виден → "Not logged in"
|
||||
- Фикс: явный `source ~/.hermes/.env` в wrapper-скрипте
|
||||
2. PATH не содержит `/opt/homebrew/bin` → Claude CLI не найден
|
||||
- Фикс: явный `export PATH="/opt/homebrew/bin:..."` в wrapper-скрипте
|
||||
3. Порт 3456 занят после перезагрузки:
|
||||
- `lsof -ti :3456 | xargs kill -9 && launchctl unload && launchctl load ...`
|
||||
4. `provider: openai` не существует в Hermes — нужно `custom`
|
||||
5. `base_url` должен включать `/v1` (Hermes дописывает `/chat/completions`)
|
||||
|
||||
**Zulip Docker:**
|
||||
- RabbitMQ 4.x Khepri WAL crash при переполнении диска → пользователи исчезают
|
||||
- Фикс: `docker system prune` (освободить место) + пересоздать volume если повреждён
|
||||
- Без log rotation логи заполняют Docker VM (~6 ГБ за несколько месяцев)
|
||||
- Mac Docker Desktop sparse disk — `docker system prune` запускает compaction
|
||||
|
||||
## Pitfalls Kraken (Docker)
|
||||
|
||||
- `HERMES_SKIP_CHOWN=1` обязателен (RPi5 не имеет прав chown в контейнере)
|
||||
- `--init` флаг обязателен (zombie reaping)
|
||||
- `--network=host` для доступа к Zulip на том же хосте
|
||||
- Vault монтируется через `-v /home/kraken/obsidian:/vault`
|
||||
- Полный набор правил → [[tech/hermes-docker-kraken]]
|
||||
|
||||
## Общее для обоих
|
||||
|
||||
- Модель: `claude-sonnet-4-6` через openclaw-claude-proxy
|
||||
- MCP: mcpvault (vault path разный, но tool API одинаковый)
|
||||
- Zulip transport: организация `zulip.mallexxx.duckdns.org`
|
||||
- Obsidian vault синхронизируется через git
|
||||
|
||||
## Связанные страницы
|
||||
|
||||
- [[tech/hermes-eagle-mac]] — детали настройки на Eagle
|
||||
- [[tech/hermes-docker-kraken]] — детали Docker деплоя на Кракен
|
||||
- [[personal-os-architecture]] — полная карта системы
|
||||
- [[tech/kraken-network]] — сетевая топология
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
---
|
||||
namespace: work
|
||||
tags:
|
||||
- agent
|
||||
- memory
|
||||
- wiki
|
||||
- synthesis
|
||||
- vault
|
||||
created: '2026-05-19'
|
||||
updated: '2026-05-19'
|
||||
type: concept
|
||||
confidence: 0.9
|
||||
sources:
|
||||
- wiki/concepts/agent-memory-architecture.md
|
||||
- wiki/concepts/vault-strategy.md
|
||||
- wiki/wiki-ingest-process.md
|
||||
- wiki/personal-os-self-modification.md
|
||||
---
|
||||
# Knowledge Lifecycle — From Session to Permanent Memory
|
||||
|
||||
Синтез: как знание движется от рабочей сессии к постоянной памяти в personal-os. Объединяет [[concepts/agent-memory-architecture]], [[concepts/vault-strategy]] и [[wiki-ingest-process]].
|
||||
|
||||
## Общая схема
|
||||
|
||||
```
|
||||
Событие/Разговор
|
||||
│
|
||||
▼
|
||||
Working Memory (контекст сессии, эфемерный)
|
||||
│ session_search + SHA256 hash
|
||||
▼
|
||||
Episodic Memory (SQLite sessions, сжатые саммари)
|
||||
│ crystallization (wiki-curation cron 02:00)
|
||||
▼
|
||||
Semantic Memory (wiki/ pages, постоянное знание)
|
||||
│ wiki-ingest 22:00 (launchd, claude -p)
|
||||
│ overflow rule (>150 chars → vault)
|
||||
▼
|
||||
Procedural Memory (skills/, workflows)
|
||||
```
|
||||
|
||||
## Три барьера кристаллизации
|
||||
|
||||
Не всё знание заслуживает кристаллизации. Барьеры:
|
||||
|
||||
| Уровень | Критерий | Место |
|
||||
|---------|----------|-------|
|
||||
| Session → Wiki | Painful to re-derive? Fits domain? Contains facts (not conversation)? | `wiki/` |
|
||||
| Wiki → MEMORY.md | Стабильный факт, ≤150 символов? | `~/.hermes/MEMORY.md` |
|
||||
| Any → Skills | Пошаговая процедура, повторяемая? | `~/.hermes/skills/` |
|
||||
|
||||
**Правило overflow:** MEMORY.md — только короткие стабильные факты. Детали → в wiki с pointer в MEMORY.md: `"media-pipeline питфолы → wiki/tech/media-pipeline-pitfalls.md"`.
|
||||
|
||||
## Три задачи vault (не смешивать)
|
||||
|
||||
1. **Vault Enrichment** — frontmatter + aliases + wikilinks на `personal/`, `family/`
|
||||
2. **LLM Wiki** — crystallization sessions → `wiki/` (этот файл про это)
|
||||
3. **Proactive Research** — агент генерирует гипотезы → `wiki/research-queue.md` → Saturday cron
|
||||
|
||||
## Риски деградации
|
||||
|
||||
- **Summarization drift:** после 3+ циклов сжатия теряются критические инструкции. Решение: vault как immutable store (не перезаписывать, только аппендить/обновлять dated sections).
|
||||
- **Repetitive behavior:** без weekly reflection агент деградирует (Generative Agents, 48ч без рефлексии). Фикс: Retrospector каждую пятницу читает corrections_log.
|
||||
- **Orphaned knowledge:** факт записан, но никогда не читается. Фикс: lint (orphan pages), провалидированные wikilinks.
|
||||
|
||||
## Инструменты в personal-os
|
||||
|
||||
| Инструмент | Функция |
|
||||
|-----------|---------|
|
||||
| wiki-curation cron (02:00) | Crystallization: sessions → wiki |
|
||||
| wiki-ingest launchd (22:00) | Synthesis: raw/ symlinks → wiki pages |
|
||||
| `session_search` | Episodic retrieval |
|
||||
| obsidian MCP | Semantic retrieval |
|
||||
| MEMORY.md | Fast facts, overflow pointers |
|
||||
| research-queue.md | Proactive research agenda |
|
||||
|
||||
## Связанные страницы
|
||||
|
||||
- [[concepts/agent-memory-architecture]] — когнитивная таксономия памяти (episodic/semantic/procedural)
|
||||
- [[concepts/vault-strategy]] — три задачи vault: enrichment, LLM wiki, proactive research
|
||||
- [[wiki-ingest-process]] — hash-based incremental ingest из raw/ symlinks
|
||||
- [[personal-os-self-modification]] — как система эволюционирует через корректировки
|
||||
@@ -0,0 +1,107 @@
|
||||
---
|
||||
title: Multi-Agent Design Patterns
|
||||
created: '2026-05-25'
|
||||
updated: '2026-05-25'
|
||||
type: concept
|
||||
namespace: work
|
||||
tags: [agent, architecture, system]
|
||||
confidence: medium
|
||||
sources:
|
||||
- wiki/concepts/executor-orchestrator.md
|
||||
- wiki/concepts/autonomous-agent-safety.md
|
||||
- wiki/concepts/agent-memory-architecture.md
|
||||
related:
|
||||
- "[[concepts/executor-orchestrator]]"
|
||||
- "[[concepts/autonomous-agent-safety]]"
|
||||
- "[[concepts/agent-memory-architecture]]"
|
||||
---
|
||||
|
||||
# Multi-Agent Design Patterns
|
||||
|
||||
Synthesis of recurring patterns across the personal-os multi-agent system
|
||||
and related agent architectures. Distilled from [[concepts/executor-orchestrator]],
|
||||
[[concepts/autonomous-agent-safety]], and [[concepts/agent-memory-architecture]].
|
||||
|
||||
## Pattern 1 — Orchestrator / Worker Split
|
||||
|
||||
**What:** One agent owns conversation state and decision authority
|
||||
(orchestrator); spawns separate worker agents for discrete tasks.
|
||||
|
||||
**Why:** Tight coupling between conversational layer and execution layer
|
||||
causes mid-task interruptions and breaks user flow. Separate concerns.
|
||||
|
||||
**In personal-os:** Eagle (orchestrator) → Executor (worker) via Zulip.
|
||||
**In psychologist app:** Narrator (orchestrator/mediator) ← Analyst (worker).
|
||||
|
||||
**Key constraint:** Worker communicates only through structured messages;
|
||||
orchestrator has the only escalation path to the human.
|
||||
|
||||
## Pattern 2 — Scope Boundary + Auto-Approve Table
|
||||
|
||||
**What:** Predefine which actions a worker may take autonomously and which
|
||||
require escalation. Publish the table explicitly.
|
||||
|
||||
**Why:** Autonomous agents fail catastrophically when they expand scope
|
||||
unexpectedly (see [[concepts/executor-security-incident]]). Explicit tables
|
||||
make failure modes visible.
|
||||
|
||||
**Implementation:** Message-type-based approval table. Unknown message types
|
||||
default to escalation, never to silent proceed.
|
||||
|
||||
| Risk Level | Agent Action |
|
||||
|---|---|
|
||||
| Low (read, build, test) | Auto-approve |
|
||||
| Medium (write external, open PR) | Auto-approve with logging |
|
||||
| High (comment on others, merge) | Always escalate |
|
||||
| Unknown | Always escalate |
|
||||
|
||||
## Pattern 3 — Memory Layer Separation
|
||||
|
||||
**What:** Separate in-session state (working memory) from cross-session
|
||||
knowledge (semantic memory) from immutable facts (procedural/episodic).
|
||||
|
||||
**Why:** Agents that blur these layers either hallucinate stable facts or
|
||||
fail to retain important session-to-session knowledge.
|
||||
|
||||
**In personal-os:** status.md (working) / wiki pages (semantic) /
|
||||
SCHEMA.md + vault-filling-guide (procedural). See [[concepts/agent-memory-architecture]].
|
||||
|
||||
**Pattern rule:** Never update semantic memory from within an active session.
|
||||
Crystallize post-session. Never trust working memory as a source of truth
|
||||
for facts (always re-derive from semantic layer at session start).
|
||||
|
||||
## Pattern 4 — Role Specialization over Generalization
|
||||
|
||||
**What:** Instead of one agent with a long system prompt covering all roles,
|
||||
split into agents with narrow, non-overlapping responsibilities.
|
||||
|
||||
**Why:** LLMs produce better outputs when role context is tight. Conflated
|
||||
roles lead to persona drift ("is it being analytical or empathetic right now?").
|
||||
|
||||
**In psychologist app:** Analyst stays in 3rd-person analytical mode;
|
||||
Narrator stays in 1st-person user-facing mode. Neither crosses the boundary.
|
||||
|
||||
**Trade-off:** Coordination overhead (structured message passing between
|
||||
agents). Worthwhile when roles genuinely conflict (analysis vs. empathy).
|
||||
|
||||
## Pattern 5 — Deterministic Algorithm for Predictable Steps
|
||||
|
||||
**What:** Identify steps that look like LLM tasks but are actually
|
||||
deterministic (sequencing, routing, counting) and implement them as code,
|
||||
not as LLM calls.
|
||||
|
||||
**Why:** LLMs are expensive and non-deterministic. Steps like "show next
|
||||
question in list" do not benefit from LLM reasoning and introduce failure modes.
|
||||
|
||||
**Examples:**
|
||||
- Question delivery in psychologist app: batch generated by Analyst,
|
||||
sequenced by UI algorithm — not an LLM call per question
|
||||
- Approval table in Executor: code switch on message type, not LLM judgment
|
||||
- Vault namespace routing: rules in tech/vault-namespace, not agent inference
|
||||
|
||||
## See Also
|
||||
|
||||
- [[concepts/executor-orchestrator]] — concrete orchestrator/worker implementation
|
||||
- [[concepts/autonomous-agent-safety]] — safety checklist derived from incidents
|
||||
- [[concepts/agent-memory-architecture]] — memory taxonomy for LLM agents
|
||||
- [[entities/psychologist-app]] — dual-agent architecture (patterns 1 and 4)
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: Multi-Host Cron Topology
|
||||
created: '2026-05-27'
|
||||
updated: '2026-05-27'
|
||||
type: concept
|
||||
namespace: personal
|
||||
tags: [system, agent, eagle, kraken, pipeline, sync]
|
||||
confidence: medium
|
||||
sources: []
|
||||
---
|
||||
|
||||
# Multi-Host Cron Topology
|
||||
|
||||
Synthesis of how scheduled jobs are distributed across Eagle and Kraken.
|
||||
Co-occurs in [[tech/hermes-eagle-mac]], [[tech/hermes-docker-kraken]],
|
||||
[[concepts/watchlist-automation]], and [[personal-os-agent-rules]].
|
||||
|
||||
## Design Principle
|
||||
|
||||
Jobs run on the host closest to their data. Eagle handles
|
||||
agent-intelligence tasks (briefs, wiki curation). Kraken handles
|
||||
media-pipeline tasks (watchlist, arr stack). Duplication is a bug.
|
||||
|
||||
## Eagle (Mac M4) — Agent Jobs
|
||||
|
||||
| Job | Schedule | Purpose |
|
||||
|-----|----------|---------|
|
||||
| `wiki-curation` | daily 02:00 | Crystallise vault → wiki (llm-wiki skill) |
|
||||
| `vault-enrichment` | daily 03:00 | Enrich personal/family notes |
|
||||
| `cross-enrichment` | daily 04:00 | Cross-namespace enrichment + memory curation |
|
||||
| `proactive-research` | Sat 05:00 | Proactive gap research |
|
||||
| `data-pipeline` | 30min, workdays | sync.js + generate-status.js → status.md |
|
||||
| `daily-brief` | workday morning | Morning brief to Zulip |
|
||||
| `inbox-triage` | workday noon | Asana inbox triage |
|
||||
| `weekly-review` | Fri evening | Weekly review |
|
||||
|
||||
Eagle crons are Hermes agent jobs — require LLM, vault access,
|
||||
Asana context. All run via Hermes native (not Docker). See
|
||||
[[tech/hermes-eagle-mac]].
|
||||
|
||||
## Kraken (RPi5) — Pipeline Jobs
|
||||
|
||||
| Job | Schedule | Purpose |
|
||||
|-----|----------|---------|
|
||||
| `watchlist-nightly` | daily 01:00 | process-thumbs + resolve + sync-down/up |
|
||||
| `watchlist-discover` | Sun 09:00 | KP API top films → watchlist Новинки |
|
||||
| `on-download-complete` | event-triggered | media-pipeline: sort + Jellyfin rescan |
|
||||
|
||||
Kraken crons are shell scripts or Python3 — deterministic, no LLM.
|
||||
Run via Hermes cron on Kraken Hermes instance (port 8642, Docker).
|
||||
See [[tech/hermes-docker-kraken]] and [[concepts/watchlist-automation]].
|
||||
|
||||
## Migration History
|
||||
|
||||
- 2026-05-27: `watchlist-nightly` and `watchlist-discover` moved from
|
||||
Eagle to Kraken. Eagle was running them via SSH to Kraken which was
|
||||
fragile and required Eagle's network path to Kraken to be stable.
|
||||
Running on Kraken directly eliminates that dependency.
|
||||
|
||||
## Pitfall: Job Duplication
|
||||
|
||||
If the same shell-script job runs on both Eagle and Kraken, the
|
||||
Radarr/Sonarr API sees double requests. Symptom: duplicate ⬇️
|
||||
entries or double-adds. Check: `hermes cronjob list` on both hosts.
|
||||
|
||||
## See Also
|
||||
|
||||
- [[personal-os-agent-rules]] — Eagle's rules and allowed writes
|
||||
- [[tech/hermes-eagle-mac]] — Eagle Hermes config
|
||||
- [[tech/hermes-docker-kraken]] — Kraken Hermes Docker setup
|
||||
- [[concepts/watchlist-automation]] — Full watchlist flow
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
# obsidian-mcp-wrapper
|
||||
|
||||
> **Файл**: `~/scripts/obsidian-mcp-wrapper.js`
|
||||
> **Назначение**: прокси-обёртка над `obsidian-mcp`, решает четыре системных бага
|
||||
|
||||
---
|
||||
|
||||
## Проблемы, которые решает
|
||||
|
||||
### 1. ZodError при инициализации (obsidian-mcp v1.0.6)
|
||||
|
||||
`obsidian-mcp` падал с ZodError сразу после запуска. Причина: Hermes отправляет
|
||||
`notifications/initialized` с полем `"id": null`, а obsidian-mcp v1.0.6 использует
|
||||
`.strict()` валидацию и не принимает лишние поля.
|
||||
|
||||
**Fix**: wrapper перехватывает все notification-сообщения (без `result`/`error`) с `id === null`
|
||||
и удаляет поле `id` перед передачей в child.
|
||||
|
||||
### 2. Race condition при gateway restart
|
||||
|
||||
При рестарте Hermes gateway поднимает новый процесс `obsidian-mcp-wrapper`. Первые
|
||||
параллельные tool-вызовы приходят пока child ещё инициализируется (~500ms) → они
|
||||
тайм-аутились, circuit breaker открывался (3 фейла → 60s cooldown).
|
||||
|
||||
**Fix**: wrapper буферизует все tool-вызовы до завершения handshake
|
||||
(`initialize` → ответ → `notifications/initialized`), потом флашит очередь.
|
||||
|
||||
### 3. Corrupted large payloads (UTF-8 chunk split)
|
||||
|
||||
При больших tool-вызовах (~200KB+) Node.js доставляет stdin в нескольких chunk-ах.
|
||||
Старый код делал string split — JSON разрезался по байтам → UTF-8 multibyte символы
|
||||
портились, `JSON.parse` падал, сообщение дропалось молча.
|
||||
|
||||
**Симптом**: `edit_note` с большим контентом тихо зависал (30s timeout), в логах:
|
||||
```
|
||||
[obsidian-wrapper] Non-JSON from Hermes (forwarding verbatim): {"jsonrpc": "2.0", "method": "tools/call", "id": 3, "params": {"name": "edit-not
|
||||
```
|
||||
|
||||
**Fix**: stdin и stdout читаются через `Buffer.concat` + `Buffer.slice` на `0x0a`.
|
||||
Строка собирается полностью до передачи в `JSON.parse`.
|
||||
|
||||
### 4. Per-call watchdog (зависший child)
|
||||
|
||||
Если child не ответил на `tools/call` / `tools/list` / `resources/*` за **5s** —
|
||||
watchdog убивает процесс. После авторестарта call автоматически уходит в голову
|
||||
очереди и ретраится.
|
||||
|
||||
**Fix**: `armWatchdog(callLine)` → `setTimeout 5000ms` → `child.kill()` → `startChild()`.
|
||||
|
||||
---
|
||||
|
||||
## Как работает
|
||||
|
||||
```
|
||||
Hermes (stdin) → wrapper → obsidian-mcp (child)
|
||||
↑ auto-restart при краше (до 10 раз)
|
||||
```
|
||||
|
||||
**Состояния**:
|
||||
- `ready = false` — child стартует, все tool-вызовы в очередь
|
||||
- `ready = true` — handshake завершён, очередь флашится, всё проходит напрямую
|
||||
|
||||
**Restart логика**:
|
||||
1. Child упал → `ready = false`, `restarts++`
|
||||
2. Новый child спавнится
|
||||
3. Wrapper реплеит сохранённый `initialize` → ждёт ответа с `serverInfo`
|
||||
4. Отправляет `notifications/initialized` (не форвардит Hermes — он не просил)
|
||||
5. `ready = true` → флаш очереди
|
||||
|
||||
**Shutdown**:
|
||||
На stdin EOF (`Hermes` закрыл процесс) — child убивается без авторестарта, wrapper выходит чисто.
|
||||
|
||||
**Логи** (все в stderr с timestamp):
|
||||
```
|
||||
[obsidian-wrapper] 2026-05-09T12:00:00.000Z Spawning obsidian-mcp (vault=/Users/admin/obsidian)
|
||||
[obsidian-wrapper] 2026-05-09T12:00:00.500Z Hermes → child (handshake complete): notifications/initialized
|
||||
[obsidian-wrapper] 2026-05-09T12:00:00.501Z Child ready — flushing queue (3 items)
|
||||
[obsidian-wrapper] 2026-05-09T12:00:01.200Z Stripped id:null from notification: notifications/initialized
|
||||
[obsidian-wrapper] 2026-05-09T12:00:06.000Z WATCHDOG: child did not respond in 5000ms for tools/call #7 — killing and restarting
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Конфиг Hermes
|
||||
|
||||
`~/.hermes/config.yaml`:
|
||||
```yaml
|
||||
obsidian:
|
||||
command: node
|
||||
args: [/Users/admin/scripts/obsidian-mcp-wrapper.js]
|
||||
```
|
||||
|
||||
Wrapper сам вызывает `/opt/homebrew/bin/obsidian-mcp /Users/admin/obsidian`.
|
||||
|
||||
---
|
||||
|
||||
## Производительность
|
||||
|
||||
- Инициализация: ~587ms (без ZodError)
|
||||
- Overhead wrapper: negligible (pure Node.js child_process, нет npm-зависимостей)
|
||||
- MAX_RETRY: 10
|
||||
- CALL_TIMEOUT_MS: 5000ms (watchdog)
|
||||
|
||||
---
|
||||
|
||||
## История
|
||||
|
||||
**2026-05-09 (1)** — создан после диагностики 58 ошибок `obsidian/... call failed` в логах Hermes.
|
||||
Корневая причина — ZodError + race condition при старте. Wrapper написан вместо патча
|
||||
исходников obsidian-mcp (патч не нужен, wrapper чище и не ломается при обновлении пакета).
|
||||
|
||||
**2026-05-09 (2)** — фикс large payload: Buffer-based line splitting вместо string split
|
||||
(`edit_note` с большим контентом молча дропался). Добавлен per-call watchdog (5s timeout → kill & retry).
|
||||
MAX_RETRY повышен с 5 до 10.
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
---
|
||||
namespace: work
|
||||
tags: [system, agent, eagle, rules]
|
||||
last_updated: 2026-04-28
|
||||
confidence: 1.0
|
||||
---
|
||||
|
||||
# Personal OS — Agent Rules & Architecture
|
||||
|
||||
Eagle (Орёл) is the Zulip-facing Hermes agent. This page documents what Eagle knows about its own environment and how to reason about it.
|
||||
|
||||
## Data Flow
|
||||
|
||||
```
|
||||
Asana API
|
||||
→ sync.js (every 30 min, workdays via Hermes cron)
|
||||
→ PostgreSQL (tasks, stories, task_edges, sync_state, task_annotations)
|
||||
→ generate-status.js
|
||||
→ ~/Developer/personal-os/asana_context.md ← raw Asana context (inbox-check)
|
||||
→ ~/context/status.md ← Eagle reads THIS
|
||||
```
|
||||
|
||||
Eagle **only reads** `~/context/status.md`. It never touches the DB directly or calls Asana API.
|
||||
|
||||
## Eagle's Knowledge Sources
|
||||
|
||||
1. `~/context/status.md` — live Asana + ActivityWatch snapshot (updated every 30 min)
|
||||
2. `~/Developer/personal-os/briefs/daily/YYYY-MM-DD.md` — today's brief
|
||||
3. `~/Developer/personal-os/briefs/weekly/plan-MONDAY.md` — weekly plan
|
||||
4. `~/obsidian/wiki/` — knowledge base (via obsidian MCP)
|
||||
5. `~/Developer/personal-os/agent/` — playbooks and rules (via file tools)
|
||||
|
||||
## Feedback Loop
|
||||
|
||||
When Alex corrects something:
|
||||
1. Edit the brief directly (show diff first, confirm before writing)
|
||||
2. If systemic → edit the prompt template in `agent/prompts/`
|
||||
3. Log correction silently to `corrections_log` table:
|
||||
|
||||
```sql
|
||||
INSERT INTO corrections_log (date, week_number, source, original_plan, correction, deferred_gids, reason_tag)
|
||||
VALUES (current_date, EXTRACT(WEEK FROM current_date)::int,
|
||||
'<source>', '<original>', '<user_message>',
|
||||
ARRAY[<gids>]::text[], '<reason_tag>');
|
||||
```
|
||||
|
||||
`source` values: `zulip_pushback` | `morning_brief_correction` | `manual`
|
||||
`reason_tag` examples: `ship-review-crunch` | `urgent-bug` | `meeting-day` | `over-estimated` | `scope-change`
|
||||
|
||||
Never announce the log insertion. It is silent instrumentation.
|
||||
|
||||
## Self-Diagnostics
|
||||
|
||||
If pipeline seems stale, Eagle can check:
|
||||
```bash
|
||||
stat -f "%Sm" ~/context/status.md
|
||||
# Should be < 40 min old on workdays
|
||||
```
|
||||
|
||||
If sync is broken:
|
||||
```bash
|
||||
bash ~/scripts/run-pipeline.sh
|
||||
# Runs sync.js + generate-status.js + healthcheck
|
||||
```
|
||||
|
||||
If wiki is stale:
|
||||
```bash
|
||||
bash ~/scripts/run-wiki-ingest.sh
|
||||
# Runs at 22:00 via launchd; call manually if needed
|
||||
```
|
||||
|
||||
## Cron Schedule (Hermes)
|
||||
|
||||
| Job | Schedule | Channel |
|
||||
|-----|----------|---------|
|
||||
| data-pipeline | */30 7-21 workdays | silent |
|
||||
| morning-brief | 08:30 workdays | #daily-brief |
|
||||
| eod-summary | 18:00 workdays | #daily-brief |
|
||||
| inbox-check | */30 9-19 workdays | #inbox |
|
||||
| weekly-plan | Mon 08:00 | #daily-brief |
|
||||
| weekly-review | Fri 17:00 | #daily-brief |
|
||||
| retrospector | Fri 17:30 | #retrospector |
|
||||
| commit-vault | 23:00 daily | silent |
|
||||
|
||||
wiki-ingest runs at 22:00 via **launchd** (not Hermes) — because it needs Mac filesystem access.
|
||||
|
||||
## Allowed File Writes
|
||||
|
||||
Eagle can write to:
|
||||
- `briefs/daily/YYYY-MM-DD.md`
|
||||
- `briefs/weekly/plan-MONDAY.md`
|
||||
- `briefs/weekly/YYYY-MM-DD.md`
|
||||
- `briefs/inbox/YYYY-MM-DD.md`
|
||||
- `agent/prompts/*.md` (with confirmation)
|
||||
- `~/obsidian/work/projects/SLUG.md`
|
||||
- `~/obsidian/work/decisions/YYYY-MM-TOPIC.md`
|
||||
- `~/obsidian/personal/`
|
||||
- `~/obsidian/family/`
|
||||
|
||||
Never write to `obsidian/wiki/` directly — that's wiki-ingest's job.
|
||||
Never write `corrections_log.md` — use the SQL INSERT above (table, not file).
|
||||
|
||||
## Activity Classification
|
||||
|
||||
Work activity is classified via `aw-projects.json`. Projects with `namespace: "personal"` (e.g., AXPressDeck, media_files_db) appear as a footnote in the Activity section of status.md but are **not** counted as work time and **not** upserted to `activity_daily`.
|
||||
|
||||
To re-classify a project (personal ↔ work), ask Eagle to edit `aw-projects.json`.
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
---
|
||||
namespace: work
|
||||
tags: [system, architecture]
|
||||
last_updated: 2026-04-28
|
||||
confidence: 1.0
|
||||
---
|
||||
|
||||
# Personal OS — Architecture
|
||||
|
||||
## Why It Exists
|
||||
|
||||
See: `personal-os-purpose.md`
|
||||
|
||||
## Agents
|
||||
|
||||
### Стратег (Strategist) — data pipeline, no conversation
|
||||
- `~/scripts/run-pipeline.sh` → sync.js + generate-status.js every 30 min (workdays via Hermes cron)
|
||||
- Writes `~/context/status.md` and `~/Developer/personal-os/asana_context.md`
|
||||
- Writes daily AW activity summaries to `activity_daily` table with quadrant classification
|
||||
- No Zulip output — pure data layer
|
||||
|
||||
### Орёл / Eagle (Tactician + reactive layer) — Hermes Zulip agent
|
||||
- Reads `~/context/status.md`, daily/weekly briefs, vault via obsidian MCP
|
||||
- Zulip streams: `daily-brief` `inbox` `focus` `executor` `projects` `journal` `retrospector`
|
||||
- Cron jobs: generate-daily-brief (07:00 MTWRF), morning-brief (08:30 MTWRF), eod-summary (18:00 MTWRF), inbox-check (30 min 09–19 MTWRF), weekly-plan (Mon 08:00), weekly-review (Fri 17:00), retrospector (Fri 17:30), executor-autonomous (every 30 min 09–18 MTWRF), commit-vault (23:00 daily)
|
||||
- Logs corrections to `corrections_log` table (SQL INSERT, silently on pushback)
|
||||
- **Never accesses Asana API directly** — only reads pre-rendered markdown and DB
|
||||
|
||||
### Ретроспектор (Retrospector) — Friday pattern analysis
|
||||
- Runs as separate Hermes cron at 17:30 Friday → `#retrospector`
|
||||
- Sources: corrections_log table, status.md subtask counts, obsidian git log, activity_daily quadrants, stories visibility metric
|
||||
- Prompt: `~/Developer/personal-os/agent/prompts/retrospector.md`
|
||||
|
||||
### Исполнитель (Executor) — two modes
|
||||
|
||||
**Manual mode**: triggered by "fix bug [GID]" in #executor
|
||||
- Single STOP gate after analysis; then autonomous: fix → build → test → draft PR → CI loop
|
||||
- Prompt: `~/Developer/personal-os/agent/prompts/executor-bug-fix.md`
|
||||
|
||||
**Autonomous mode**: hourly cron (09–18 workdays) → #executor
|
||||
- Maintains a queue from: My Hack Days, Watched, O-L Backlog, stale My Tasks
|
||||
- Analyzes tasks → posts "[Auto] Ready to start" with complexity/feasibility
|
||||
- Waits for "go {gid}" to start; no reply = moves to next analysis
|
||||
- Max 1 active fix at a time; handles CI/review on open draft PRs
|
||||
- Stops touching a PR once Alex moves it out of draft or pushes commits
|
||||
- No Asana comments; no touching other people's PRs
|
||||
- Queue state in `executor_queue` table; live plan: `~/Developer/personal-os/briefs/executor-queue.md`
|
||||
- Prompt: `~/Developer/personal-os/agent/prompts/executor-autonomous.md`
|
||||
|
||||
**Shared**:
|
||||
- Worktree at `~/DuckDuckGo/apple-browsers.git/.claude/worktrees/executor-{gid}-{slug}/`
|
||||
- Always `git fetch origin main` + branch from `origin/main`
|
||||
- State machine in `executor_runs` table
|
||||
- For UI tests: spins VM via ddg-vm MCP (virfield), cleans up on PR close/merge
|
||||
|
||||
**Eagle commands in #executor**:
|
||||
| Command | Effect |
|
||||
|---------|--------|
|
||||
| `fix bug {gid}` | Spawn manual executor (separate agent) |
|
||||
| `go {gid}` | Approve autonomous task → starts next tick |
|
||||
| `skip {gid}` | Skip task in queue |
|
||||
| `stop` | Pause current autonomous task |
|
||||
| `next` | Pause current, analyze next |
|
||||
| `resume {gid}` | Re-queue paused task |
|
||||
| `queue` | Show current executor-queue.md |
|
||||
|
||||
## Data Flow
|
||||
|
||||
```
|
||||
Asana API
|
||||
→ sync.js (30 min, Hermes cron, silent)
|
||||
→ PostgreSQL: tasks, stories, task_edges, sync_state, task_annotations
|
||||
|
||||
ActivityWatch (local daemon)
|
||||
→ generate-status.js (reads AW HTTP API)
|
||||
→ activity_daily (upserts daily project/quadrant summaries)
|
||||
|
||||
generate-status.js
|
||||
→ ~/context/status.md ← Eagle reads this
|
||||
→ ~/Developer/personal-os/asana_context.md ← inbox-check reads this
|
||||
|
||||
Eagle corrections in Zulip
|
||||
→ corrections_log table (INSERT on pushback, no announcement)
|
||||
→ Retrospector reads weekly
|
||||
|
||||
Executor runs
|
||||
→ executor_runs table (state machine)
|
||||
→ ~/DuckDuckGo/apple-browsers.git/.claude/worktrees/
|
||||
→ GitHub draft PRs (--draft, --assignee @me, no reviewers)
|
||||
```
|
||||
|
||||
## Key File Locations
|
||||
|
||||
| File / Path | Purpose |
|
||||
|-------------|---------|
|
||||
| `~/context/status.md` | Live Asana + AW snapshot. Written by generate-status.js. Eagle reads this. |
|
||||
| `~/Developer/personal-os/asana_context.md` | Raw Asana context for inbox-check |
|
||||
| `~/Developer/personal-os/agent/prompts/` | All agent prompts (Hermes cron + on-demand) |
|
||||
| `~/.hermes/SOUL.md` | Eagle's identity, rules, vault write permissions |
|
||||
| `~/Developer/personal-os/config.json` | Asana workspace/user GIDs, section GIDs |
|
||||
| `~/Developer/personal-os/aw-projects.json` | ActivityWatch project classification rules |
|
||||
| `~/.hermes/config.yaml` | Hermes config: model, MCP servers, cron settings |
|
||||
| `~/obsidian/` | Vault — git repo, NAS remote, obsidian-mcp for search |
|
||||
| `~/Developer/personal-os/logs/` | heartbeat.log, wiki-ingest-YYYY-MM-DD.md |
|
||||
| `~/Developer/personal-os/briefs/` | daily/, weekly/, inbox/ — agent-written brief files |
|
||||
| `~/DuckDuckGo/apple-browsers.git/` | Browser repo (bare), worktrees as sibling dirs |
|
||||
| `~/DuckDuckGo/apple-browsers.git/.claude/worktrees/` | Executor worktrees |
|
||||
|
||||
## Database Tables
|
||||
|
||||
| Table | Written by | Read by |
|
||||
|-------|-----------|---------|
|
||||
| tasks | sync.js | generate-status.js, executor, status queries |
|
||||
| stories | sync.js | generate-status.js, executor, retrospector |
|
||||
| task_edges | sync.js | subtask traversal |
|
||||
| sync_state | sync.js | generate-status.js header |
|
||||
| task_annotations | manual / Eagle | generate-status.js irrelevant filter |
|
||||
| activity_daily | generate-status.js | retrospector quadrant drift |
|
||||
| corrections_log | Eagle on pushback | retrospector pattern analysis |
|
||||
| executor_runs | executor (manual + autonomous) | generate-status.js active runs, executor state machine |
|
||||
| executor_queue | executor-autonomous | generate-status.js queue display, autonomous tick state |
|
||||
|
||||
## MCP Servers
|
||||
|
||||
**Hermes agents:**
|
||||
| Name | Command | Used for |
|
||||
|------|---------|---------|
|
||||
| obsidian | `/opt/homebrew/bin/obsidian-mcp ~/obsidian` | Vault search, read, write |
|
||||
|
||||
**Claude Code / `claude -p` sessions:**
|
||||
| Name | Command | Used for |
|
||||
|------|---------|---------|
|
||||
| ddg-vm | `npx tsx ~/Developer/virfield/server/mcp-server.ts` | VM lifecycle for UI tests |
|
||||
| obsidian | `/opt/homebrew/bin/obsidian-mcp ~/obsidian` | Vault access |
|
||||
|
||||
## launchd Agents
|
||||
|
||||
| Label | Schedule | Purpose |
|
||||
|-------|----------|---------|
|
||||
| personal.os.heartbeat | :05 every hour | Runs run-pipeline.sh directly (no Claude auth needed); also triggers missed Hermes jobs after wake |
|
||||
| personal.os.wiki-ingest | 22:00 daily | `claude -p` wiki synthesis (needs filesystem access) |
|
||||
|
||||
## Vault Structure
|
||||
|
||||
```
|
||||
~/obsidian/
|
||||
├── wiki/ ← LLM-generated + hand-written (confidence: 1.0 = never overwrite)
|
||||
│ ├── personal-os-architecture.md (this file)
|
||||
│ ├── personal-os-purpose.md
|
||||
│ ├── personal-os-agent-rules.md
|
||||
│ ├── personal-os-self-modification.md
|
||||
│ ├── personal-os-sync-pipeline.md
|
||||
│ ├── personal-os-schema.md (auto-generated)
|
||||
│ ├── ddg-asana-workflow.md
|
||||
│ └── vault-filling-guide.md
|
||||
├── raw/ ← symlinks to external source files (schema.sql, etc.)
|
||||
├── work/
|
||||
│ └── wiki/
|
||||
│ └── apple-browsers/ ← .cursor/rules copies (.md) + vm-ui-testing.md
|
||||
└── personal/ family/
|
||||
```
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
source: raw/personal-os-catchup-plan-2026-04-27.md
|
||||
content_hash: 5081ceb719104e1b7c6b5edd642c1daf38b3fd7cf1a96442a00516e7cdb15379
|
||||
namespace: work
|
||||
last_synced: 2026-04-28
|
||||
confidence: 0.8
|
||||
tags: [personal-os, planning, catchup, executor, retrospector]
|
||||
---
|
||||
|
||||
# Personal OS — Catch-Up Plan (2026-04-27)
|
||||
|
||||
Handoff document for the executor agent: what to build next, in what order, and why. Companion to [[personal-os-state-2026-04-27]].
|
||||
|
||||
## Context
|
||||
|
||||
Alex (macOS Browser DRI at DuckDuckGo, ADHD profile, async-first) needs the Personal OS to hold work context outside his head — so comments don't get lost, focus is protected, and career goals don't get crushed by current projects.
|
||||
|
||||
Original four-agent design — Стратег / Тактик / Ретроспектор / Исполнитель. Stratan and Retrospector ✅ shipped; Tactic 🟡 partial; Executor 🟡 v1 with manual STOP gates only.
|
||||
|
||||
**Catching up on:** career tracking, inbox triage hygiene, focus-aware ping gating, structured `corrections_log`, quadrant column on `activity_daily`, Executor v2, MS365 MCP wiring, vault consolidation, Executor PR template.
|
||||
|
||||
## Architectural principle (reinforced)
|
||||
|
||||
No new YAML files. No new shell scripts. No new launchd plists. Everything extends existing components: postgres tables, `generate-status.js` queries, prompt files in `agent/prompts/`, Eagle's `SOUL.md` rules. See [[personal-os-architecture]].
|
||||
|
||||
## Six deliverables (dependency-ordered)
|
||||
|
||||
### 1.1 Schema migration
|
||||
|
||||
Three changes — see [[personal-os-schema]]:
|
||||
- `corrections_log` (id, date, week_number, source, original_plan, correction, deferred_gids[], reason_tag) — Tactic writes, Retrospector aggregates
|
||||
- `executor_runs` (task_gid, worktree_path, branch_name, pr_url, state, ci_attempts, thread_id, timestamps) — state machine for autonomous Executor
|
||||
- `activity_daily.quadrant` column — values: `project | aor | career | strategy | other`
|
||||
|
||||
Why tables not markdown: Retrospector needs aggregates ("X deferred 3+ times in 4 weeks"); SQL trivial, LLM-parsing markdown each time is fragile.
|
||||
|
||||
### 1.2 generate-status.js — three new SQL blocks
|
||||
|
||||
- **Career: Advisor pipeline** — open tasks tagged "Project Advisor", surface as `## ⚡ Career — Advisor Opportunities`
|
||||
- **Career: Visibility gaps** — Assessments / O-N / O-L tasks where Alex hasn't commented in 7+ days
|
||||
- **Quadrant classification at AW write time** — `PROJECT_TO_QUADRANT` map, with current top-priority project read dynamically from config (not hardcoded — drifts wrong silently otherwise)
|
||||
|
||||
### 1.3 inbox-triage.md — STEP 0 gating + overdue exclusion
|
||||
|
||||
Two-part gate added at the top of the prompt (not a wrapper script — wrapper bypasses agent judgment):
|
||||
|
||||
- **Focus check**: read latest `activity_daily` focus_score + active calendar event. Skip if in meeting, or focus_score > 60 in editor for >20min, unless 🔴 sections older than 3h.
|
||||
- **Scope filter**: inbox = NEW signal only. Exclude overdue (those belong in daily-brief), already-responded, system events, bot noise. Include only new human comments, new assignments, ship/TD status changes, mentions.
|
||||
|
||||
### 1.4 retrospector.md — three new SQL blocks
|
||||
|
||||
Replace markdown `corrections_log.md` reads with:
|
||||
- **Correction patterns** — `reason_tag` GROUP BY HAVING COUNT >= 3 over 4 weeks
|
||||
- **Quadrant drift** — 4-week × quadrant pivot; flag any quadrant <5% for 3+ weeks (career gets the strongest flag)
|
||||
- **Visibility this week** — count of strategic comments by Alex; surfaces career signal as a measurable proxy for the EP3 "thin Strategic Leadership" gap
|
||||
|
||||
### 1.5 Eagle SOUL.md — corrections_log writeback
|
||||
|
||||
When user pushes back in any channel, BEFORE responding: classify pushback type, infer reason_tag from message context, INSERT into `corrections_log` via psql, then respond normally. Don't announce the insert. Eagle is already in conversation — capturing in-context beats post-hoc log parsing.
|
||||
|
||||
### 1.6 Executor v2 — single STOP gate, then autonomous
|
||||
|
||||
Replace v1's many gates with **one gate after Analysis** (the consequential decision). Then: fix → build → test → branch (`executor/<gid>-<slug>`) → push → `gh pr create --draft --assignee @me` (no `--reviewer`) → self-review comment → CI poll loop (every 10min, max 3 auto-fix attempts).
|
||||
|
||||
**Hard prohibitions**: never push non-`executor/*` branches, never non-draft PRs, never `--reviewer`, never `gh pr merge`, never modify `.github/` or deps without thread confirmation.
|
||||
|
||||
Draft + assignee=@me + no reviewers gives a durable, ADHD-friendly surface (visible in "assigned to me", ignored by branch-protection auto-merge, no reviewer noise).
|
||||
|
||||
### 1.7 MS365 MCP into Hermes
|
||||
|
||||
One-time Hermes MCP config change benefits multiple prompts (inbox-triage, weekly-plan, daily-brief). Used in STEP 0 to detect active meetings via `outlook_calendar_search`. MS365 chosen over Google because it's the actual workspace.
|
||||
|
||||
### 1.8 Vault consolidation
|
||||
|
||||
Move into `~/obsidian/work/wiki/`: process docs from `~/Developer/personal-os/`, the UI testing skill, copies of `apple-browsers/.cursor/*.mdc` files. Decision rule: process/convention/knowledge → vault; runtime configs → original location. **Verify each prompt/skill end-to-end after move** — silent path-reference breakage is the failure mode. Copy `.mdc` (don't move) — Cursor still reads them in place.
|
||||
|
||||
### 1.9 PR template — Executor variant
|
||||
|
||||
Team's PR template requires manual confirmation of task/reviewer/description. For autonomous flow all three are deterministic. Use **inline template generation** (Option B) in the Executor prompt — auto-generate body from analysis + diff. Draft state is the safety net; Alex polishes on flip-to-ready.
|
||||
|
||||
## Execution order
|
||||
|
||||
1, 2, 4, 5 unblocked. 3 needs MS365 (#2). 6 needs schema (#1) + quadrants (#5). 7 needs schema (#1). 8, 9 unblocked. 10 (Executor v2) needs 1, 8, 9. **No new Discord channels needed.**
|
||||
|
||||
## "Done" looks like
|
||||
|
||||
Friday Retrospector says specific things ("deferred bug work 4× for ship-review-crunch"); inbox shows only new human signals (silent during meetings); `## ⚡ Career` section in status.md; pushback creates one `corrections_log` row; `fix [task]` in `#executor` produces an analysis post → approval gate → draft PR within 30min for trivial bugs; vault contains all `.mdc` + UI skill + process docs reachable via obsidian-mcp.
|
||||
|
||||
## Related
|
||||
|
||||
[[personal-os-state-2026-04-27]] [[personal-os-architecture]] [[personal-os-schema]] [[personal-os-agent-rules]] [[personal-os-self-modification]] [[ddg-asana-workflow]]
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
---
|
||||
namespace: work
|
||||
tags: [system, purpose, adhd, design]
|
||||
last_updated: 2026-04-28
|
||||
confidence: 1.0
|
||||
---
|
||||
|
||||
# Personal OS — Purpose
|
||||
|
||||
## Why It Exists
|
||||
|
||||
Alex is a macOS Browser Developer / DRI at DuckDuckGo. Async-first company, primary channels Asana and Slack. ADHD profile.
|
||||
|
||||
The Personal OS exists to hold work context **outside Alex's head** — so that context isn't lost during deep focus, transitions, or when switching back from a meeting.
|
||||
|
||||
Without it, three things happen regularly:
|
||||
1. A comment sits on an Asana task for days — Alex never saw it because he was in Xcode
|
||||
2. A career-level task (Ship Review, Tech Design review) gets deferred until it becomes a bottleneck
|
||||
3. End of day: "what did I actually do today?" — no clear answer, no signal for tomorrow
|
||||
|
||||
## Design Principles
|
||||
|
||||
**Principle 1 — Context is the product, not notifications.**
|
||||
The system's job is to maintain a live, accurate picture of what's happening. Eagle reads that picture and speaks when something actually matters. It is not a notification firehose.
|
||||
|
||||
**Principle 2 — Protect focus.**
|
||||
If Alex is in Xcode or Cursor, don't interrupt unless there's a red signal older than 3 hours. ADHD makes context switches expensive — the system should absorb noise, not amplify it.
|
||||
|
||||
**Principle 3 — No hallucinated state.**
|
||||
Eagle never guesses about Asana tasks. All Asana data flows through sync.js → PostgreSQL → generate-status.js → status.md. Eagle reads markdown, not the API. This means the data is always consistent and never stale by more than 30 minutes.
|
||||
|
||||
**Principle 4 — Career goals are first-class.**
|
||||
The system explicitly tracks: Project Advisor opportunities, visibility gaps (Ship Reviews where Alex hasn't commented), Tech Design reviews pending. These surface in status.md and the daily brief. Without explicit tracking they'd be invisible.
|
||||
|
||||
**Principle 5 — No new infra.**
|
||||
The system has enough moving parts. New capabilities go into existing components: Postgres tables, generate-status.js queries, prompt files in `agent/prompts/`, SOUL.md rules. No new YAML files, no new shell scripts, no new launchd plists unless there is no other way.
|
||||
|
||||
## The Four Agents
|
||||
|
||||
| Agent | Role | When |
|
||||
|-------|------|------|
|
||||
| **Стратег (Strategist)** | Data pipeline — reads Asana + ActivityWatch, writes status.md | Every 30 min, workdays |
|
||||
| **Орёл / Eagle (Tactician)** | Discord-facing reactive agent — briefs, inbox, focus gating | Cron + on-demand |
|
||||
| **Ретроспектор (Retrospector)** | Weekly pattern analysis — corrections, quadrant drift, visibility | Friday 17:30 |
|
||||
| **Исполнитель (Executor)** | On-demand bug-fix worker — analysis → fix → PR → CI loop | Explicit trigger only |
|
||||
|
||||
Eagle is the only agent Alex directly talks to. The others are background infrastructure.
|
||||
|
||||
## What "Work Context" Means
|
||||
|
||||
The system tracks four categories, mapped to `activity_daily.quadrant`:
|
||||
|
||||
| Quadrant | What it covers | Why it matters |
|
||||
|----------|---------------|----------------|
|
||||
| `project` | The current top-priority feature/bug | Time here should dominate most weeks |
|
||||
| `aor` | AOR maintenance: code review, bug fixes, blocklist | Required to keep the area healthy |
|
||||
| `career` | Project Advisor, Ship Reviews, Tech Design, visibility | Easily deferred; system forces it into view |
|
||||
| `strategy` | Planning, Personal OS, Asana organisation | Meta-work; needs a floor, not a ceiling |
|
||||
|
||||
Personal pet projects (AXPressDeck, media_files_db, etc.) are tracked separately and not counted as work time. See `aw-projects.json` (`namespace: "personal"` entries).
|
||||
|
||||
## What It Does Not Do
|
||||
|
||||
- Does not make decisions for Alex — it informs
|
||||
- Does not send notifications during focus (deep focus gate in inbox STEP 0)
|
||||
- Does not access Asana API directly from Eagle — only reads pre-rendered status.md
|
||||
- Does not create PRs, commits, or messages without explicit confirmation (except Executor, after its single approval gate)
|
||||
|
||||
## See Also
|
||||
|
||||
- `personal-os-architecture.md` — full technical architecture, agents, data flow, file locations
|
||||
- `personal-os-agent-rules.md` — Eagle's operating rules and allowed writes
|
||||
- `personal-os-self-modification.md` — how Eagle (and Alex) can evolve the system
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
---
|
||||
source: raw/schema.sql
|
||||
content_hash: 71b47e47b483834c887de14c76d5b16506d90ad64198d76a92a7b3731132dab8
|
||||
namespace: work
|
||||
last_synced: 2026-04-27
|
||||
confidence: 0.9
|
||||
tags: [schema, postgres, database, asana, wiki]
|
||||
---
|
||||
|
||||
# Personal OS Database Schema
|
||||
|
||||
Postgres schema (`personal_os`) backing the Asana mirror, agent annotations,
|
||||
file ingestion pipeline, semantic wiki, and memory store. Requires the
|
||||
`vector` (pgvector) and `pg_trgm` extensions. Bootstrap with
|
||||
`psql -U admin -d personal_os -f schema.sql`.
|
||||
|
||||
## Asana mirror
|
||||
|
||||
- **`tasks`** — one row per Asana task GID, upserted on each sync.
|
||||
Stores due/start dates, completion, My Tasks section, assignee/creator,
|
||||
primary project, timestamps, full `raw_json`, plus a `source` enum:
|
||||
`my_tasks` (assigned to me) > `delegated` (I created, others assigned)
|
||||
> `following` (CC'd) > `project` (project member, fallback).
|
||||
Sync bookkeeping: `fetched_at`, `stories_fetched_before` cursor.
|
||||
- **`stories`** — raw event log per task, keyed by Asana story GID.
|
||||
`resource_subtype` covers comments, assignment changes, due-date edits,
|
||||
section moves, dependency edits, attachments, completion, etc.
|
||||
- **`task_edges`** — directed graph between tasks. `relation_type`:
|
||||
`subtask`, `dependency` (blocked-by), `dependent` (blocking),
|
||||
`project_sibling`, `mention` (referenced in a story).
|
||||
`related_gid` may not yet exist in `tasks`.
|
||||
- **`task_annotations`** — agent or user notes per (task, annotation_type).
|
||||
Types: `irrelevant`, `watching`, `needs_action`, `snoozed`
|
||||
(with `snoozed_until`). Never written by the fetcher.
|
||||
- **`sync_state`** — one row per sync stream
|
||||
(`my_tasks`, `following`, `delegated`, `project:{gid}`,
|
||||
`workspace_events`). Tracks cursor, last sync, last full sync.
|
||||
- **`task_embeddings`** — pgvector(1024) per task, HNSW index with
|
||||
cosine ops; populated separately from sync.
|
||||
|
||||
The **`active_tasks`** view filters out completed and irrelevant tasks
|
||||
and tasks snoozed past today, ordered by source priority then due date
|
||||
then modified-at. Starting point for daily review.
|
||||
|
||||
## Phase 0 additions (2026-04-27)
|
||||
|
||||
All core tables gain `namespace TEXT NOT NULL DEFAULT 'work'` so a single
|
||||
DB can serve work/personal/family contexts. `tasks` also gets
|
||||
`possibly_deleted` and `last_seen_in_full_sync` to track tasks that
|
||||
disappear between full syncs without explicit deletion events.
|
||||
|
||||
## File ingestion → wiki
|
||||
|
||||
- **`file_references`** — files (via macOS security-scoped
|
||||
`bookmark_data` + cached `last_known_path`) and web URLs
|
||||
queued for wiki ingestion. Tracks `content_hash` (SHA256),
|
||||
`mime_type`, `tags`, a `modification_log` JSONB, and a
|
||||
`wiki_stale` flag the [[wiki-ingest-process]] consumes.
|
||||
Namespace-checked (`work`/`personal`/`family`).
|
||||
- **`wiki_pages`** — LLM-synthesised markdown, never a raw copy.
|
||||
Has `sources` JSONB (file_ref/url/title), `confidence` float,
|
||||
`superseded_by` self-FK for version chains, `stale` flag,
|
||||
`last_synced_hash`, and a pgvector(1536) embedding for semantic
|
||||
search. The optional ivfflat index is left commented; rebuild
|
||||
once the table has 1000+ rows.
|
||||
|
||||
## Memory store
|
||||
|
||||
**`memory_store`** — semantic memory from Discord, Claude sessions,
|
||||
and manual entries. `type` ∈ {fact, preference, decision, person},
|
||||
`source` ∈ {discord, claude, manual}, with entities JSONB,
|
||||
confidence, optional `expires_at` (null = permanent), and a
|
||||
pgvector(1536) embedding. Used by Hermes for cross-session context.
|
||||
|
||||
## Indexing notes
|
||||
|
||||
- Trigram GIN on `tasks.name` enables fuzzy task search.
|
||||
- `tasks_due_on` is partial (only non-completed tasks).
|
||||
- pgvector embedding indexes for `wiki_pages` and `memory_store`
|
||||
are deferred until the tables have meaningful row counts.
|
||||
- `task_embeddings` uses HNSW; the wiki/memory stores use ivfflat
|
||||
(commented) — different recall/build trade-off per workload.
|
||||
|
||||
## Key constraints
|
||||
|
||||
- All `namespace` columns are CHECK-constrained to
|
||||
`('work','personal','family')` on the new (Phase 0+) tables.
|
||||
- Cascade deletes flow from `tasks` → `stories`, `task_edges`,
|
||||
`task_annotations`, `task_embeddings`.
|
||||
|
||||
## Related
|
||||
[[personal-os-architecture]] [[wiki-ingest-process]]
|
||||
@@ -0,0 +1,152 @@
|
||||
---
|
||||
namespace: work
|
||||
tags: [system, self-modification, eagle, meta]
|
||||
last_updated: 2026-04-28
|
||||
confidence: 1.0
|
||||
---
|
||||
|
||||
# Personal OS — Self-Modification Guide
|
||||
|
||||
How Eagle (and Alex via Claude Code) can safely evolve the system without breaking it.
|
||||
|
||||
**Rule**: Always show a diff/draft first, confirm, then apply. Commit the vault after.
|
||||
|
||||
---
|
||||
|
||||
## What Eagle Can Change (no approval needed — show draft first)
|
||||
|
||||
### 1. Prompt files (`~/Developer/personal-os/agent/prompts/*.md`)
|
||||
|
||||
Eagle reads and writes its own prompts. When Alex says "add X to the morning brief" or "change how inbox triage handles Y":
|
||||
|
||||
1. Read the current prompt: `~/Developer/personal-os/agent/prompts/<name>.md`
|
||||
2. Draft the change, show the diff in Discord
|
||||
3. On confirmation: write the file
|
||||
4. Commit: `cd ~/Developer/personal-os && git add agent/prompts/<name>.md && git commit -m "[YYYY-MM-DD] prompt: <description>"`
|
||||
|
||||
**Do not** change `executor-bug-fix.md` without explicit confirmation — it controls autonomous code changes.
|
||||
|
||||
### 2. SOUL.md (`~/.hermes/SOUL.md`)
|
||||
|
||||
Eagle's identity, rules, and operating context. Eagle can propose changes to:
|
||||
- Allowed vault write paths
|
||||
- Focus gating rules
|
||||
- Channel routing
|
||||
|
||||
Same flow: draft → confirm → write → no vault commit needed (SOUL.md is outside vault).
|
||||
|
||||
### 3. ActivityWatch project rules (`~/Developer/personal-os/aw-projects.json`)
|
||||
|
||||
To classify a new project as work or personal:
|
||||
1. Read current `aw-projects.json`
|
||||
2. Add entry with `project`, `namespace` (omit for work, `"personal"` for pet projects), and `rules`
|
||||
3. Show draft, confirm, write
|
||||
4. Commit: `cd ~/Developer/personal-os && git add aw-projects.json && git commit -m "[YYYY-MM-DD] aw: classify <project>"`
|
||||
|
||||
The `namespace: "personal"` field excludes a project from work activity in status.md and activity_daily.
|
||||
|
||||
### 4. Vault knowledge pages (`~/obsidian/wiki/`, `~/obsidian/work/wiki/`)
|
||||
|
||||
Eagle does **not** write to `wiki/` — that's the wiki-ingest job (22:00 launchd). But Eagle **can** write to:
|
||||
- `~/obsidian/work/projects/SLUG.md` — project notes
|
||||
- `~/obsidian/work/decisions/YYYY-MM-TOPIC.md` — decisions
|
||||
- `~/obsidian/personal/` — personal notes
|
||||
- `~/obsidian/family/` — family docs
|
||||
|
||||
After writing: `cd ~/obsidian && git add -A && git commit -m "[YYYY-MM-DD] <description>"`
|
||||
|
||||
### 5. Correction logging (corrections_log table)
|
||||
|
||||
Eagle silently logs a row whenever Alex pushes back on a suggestion. No approval needed — this is continuous background instrumentation, not a visible change.
|
||||
|
||||
```sql
|
||||
INSERT INTO corrections_log (date, week_number, source, original_plan, correction, deferred_gids, reason_tag)
|
||||
VALUES (current_date, EXTRACT(WEEK FROM current_date)::int,
|
||||
'<source>', '<original>', '<user_message>',
|
||||
ARRAY[<gids>]::text[], '<reason_tag>');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What Requires Approval (always confirm explicitly)
|
||||
|
||||
### Schema changes (PostgreSQL)
|
||||
|
||||
New tables, columns, or indexes require a SQL migration. Pattern:
|
||||
1. Draft the SQL in Discord, explain the purpose
|
||||
2. On "go": run via `psql personal_os -c "..."`
|
||||
3. Update `~/obsidian/wiki/personal-os-schema.md` (this is auto-generated at 22:00, but a manual update is fine)
|
||||
|
||||
Current tables: tasks, stories, task_edges, sync_state, task_annotations, activity_daily, corrections_log, executor_runs.
|
||||
|
||||
### New Hermes cron jobs
|
||||
|
||||
Add to `~/.hermes/config.yaml`. Format:
|
||||
```yaml
|
||||
crons:
|
||||
- id: my-new-job
|
||||
schedule: "30 9 * * 1-5"
|
||||
prompt: |
|
||||
Run: ~/Developer/personal-os/agent/prompts/my-prompt.md
|
||||
channel: "#channel-name"
|
||||
```
|
||||
Must confirm before writing — a broken cron syntax silently prevents Hermes from starting.
|
||||
|
||||
### New launchd agents (`~/Library/LaunchAgents/`)
|
||||
|
||||
New plist files. Rarely needed — only if a job requires Mac filesystem access and can't run inside Hermes (e.g., wiki-ingest). Confirm before writing and before loading with `launchctl`.
|
||||
|
||||
### Executor prompt (`executor-bug-fix.md`)
|
||||
|
||||
Controls autonomous code changes. Changes here need explicit approval because a mistake could cause the Executor to behave incorrectly on real PRs.
|
||||
|
||||
### Hermes model or MCP configuration (`~/.hermes/config.yaml`)
|
||||
|
||||
Model upgrades, new MCP servers. Show the diff; confirm before writing.
|
||||
|
||||
---
|
||||
|
||||
## How generate-status.js Gets Extended
|
||||
|
||||
When a new data signal should appear in status.md:
|
||||
|
||||
1. Add a SQL query function (e.g., `getNewSignal()`)
|
||||
2. Add a render function (e.g., `renderNewSignal(data)`)
|
||||
3. Add both to `main()`: Promise.all for the query, the render call in `parts`
|
||||
4. Test: `node ~/Developer/personal-os/generate-status.js` and read `~/context/status.md`
|
||||
5. Commit
|
||||
|
||||
**Never** edit generate-status.js to change how Asana sync works — that's sync.js territory.
|
||||
|
||||
---
|
||||
|
||||
## Debugging the System
|
||||
|
||||
| Symptom | Check |
|
||||
|---------|-------|
|
||||
| status.md is stale (> 40 min old) | `stat -f "%Sm" ~/context/status.md` — if old, run `bash ~/scripts/run-pipeline.sh` |
|
||||
| Hermes cron didn't fire | Check heartbeat: `tail -20 ~/Developer/personal-os/logs/heartbeat.log` |
|
||||
| Eagle gave wrong Asana data | The DB may be stale — run `node ~/Developer/personal-os/sync.js` manually |
|
||||
| activity_daily missing today | generate-status.js runs upsert — check `select * from activity_daily where bucket_day = current_date` |
|
||||
| corrections_log empty | Eagle only inserts on pushback — expected to be sparse |
|
||||
| Executor in stuck state | `SELECT id, state, updated_at FROM executor_runs WHERE state NOT IN ('complete','abandoned');` |
|
||||
|
||||
---
|
||||
|
||||
## Safe Operations Checklist
|
||||
|
||||
Before making any system change:
|
||||
- [ ] Read the current file/config before proposing a change
|
||||
- [ ] Show the diff, not just a description
|
||||
- [ ] Wait for explicit confirmation
|
||||
- [ ] Apply the change
|
||||
- [ ] Verify: run the component or check output
|
||||
- [ ] Commit if in a git repo (`personal-os` or `obsidian`)
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- `personal-os-architecture.md` — full system map
|
||||
- `personal-os-agent-rules.md` — Eagle's rules and channel routing
|
||||
- `personal-os-purpose.md` — why this system exists
|
||||
@@ -0,0 +1,99 @@
|
||||
---
|
||||
source: raw/personal-os-state-2026-04-27.md
|
||||
content_hash: 1640cb63145b49263b9476e2c39fe443ac561fdf6c27faccc50cfc8568552614
|
||||
namespace: work
|
||||
last_synced: 2026-04-28
|
||||
confidence: 0.8
|
||||
tags: [personal-os, state, executor, infrastructure]
|
||||
---
|
||||
|
||||
# Personal OS — State Snapshot (2026-04-27)
|
||||
|
||||
Current state of the system after the [[personal-os-catchup-plan-2026-04-27]] was executed. Supersedes earlier state and v3 plan documents.
|
||||
|
||||
## Infrastructure (✅ live)
|
||||
|
||||
- **Postgres on Mac** — tables: `tasks`, `stories`, `task_edges`, `sync_state`, `signal_queue`, `activity_daily` (+ `quadrant` col), plus new `corrections_log`, `executor_runs`. See [[personal-os-schema]].
|
||||
- **sync.js** — 4-source Asana fetch, incremental deltas, sweepRecentlyCompleted
|
||||
- **generate-status.js** — DB → `status.md` + `asana_context.md`; live blocks: 5 advisor opps, 8 visibility gaps, 10 executor queue items in first run
|
||||
- **ActivityWatch** — DDG browser + Xcode watchers
|
||||
- **Hermes v0.11.x** — launchd daemon `ai.hermes.gateway`, Claude Code OAuth, `claude-sonnet-4-6`
|
||||
- **Discord bot Орёл#0898** — channels `#daily-brief #inbox #focus #executor #projects #journal #retrospector`
|
||||
|
||||
See [[personal-os-architecture]] and [[personal-os-sync-pipeline]] for component detail.
|
||||
|
||||
## Schedule
|
||||
|
||||
**Hermes cron jobs (8):** data-pipeline (every 30min), morning-brief (08:30), eod-summary (18:00), inbox-check (every 30min, 9-19), weekly-plan (Mon 08:00), weekly-review (Fri 17:00), retrospector (Fri 17:30 → `#retrospector`), commit-vault (23:00).
|
||||
|
||||
**launchd agents (Mac-level, fire on wake):**
|
||||
- `personal.os.heartbeat` — :05 hourly watchdog, triggers missed Hermes one-per-day jobs
|
||||
- `personal.os.wiki-ingest` — 22:00 daily, runs local `claude -p` (filesystem access requires local subprocess, not Hermes API agent)
|
||||
|
||||
## Vault
|
||||
|
||||
`~/obsidian/` — git remote `ssh://truenas_admin@mallexxx.duckdns.org/mnt/RED_2TB/storage/git/obsidian-vault.git`. Layout: `wiki/` (LLM-generated + hand-written, see [[wiki-ingest-process]]), `raw/` (symlinks), `work/` and `personal/` (Eagle-writable per [[vault-filling-guide]]), `family/`. Phase 8 of catchup added 43 `.cursor/rules/*.mdc` files under `work/wiki/apple-browsers/`.
|
||||
|
||||
## Catch-up plan execution status
|
||||
|
||||
| # | Item | Status |
|
||||
|---|------|--------|
|
||||
| 1 | Schema (corrections_log, executor_runs, quadrant) | ✅ |
|
||||
| 2 | MS365 MCP in Hermes | 🟡 cloud connector only; no local npm package; available to `claude -p` jobs once added to CLI env |
|
||||
| 3 | inbox-triage STEP 0 | ✅ |
|
||||
| 4 | generate-status: advisor + visibility + executor queue | ✅ |
|
||||
| 5 | generate-status: quadrant + activity_daily upsert | ✅ |
|
||||
| 6 | retrospector SQL blocks | ✅ |
|
||||
| 7 | SOUL.md corrections_log INSERT | ✅ |
|
||||
| 8 | Vault consolidation (43 .mdc) | ✅ |
|
||||
| 9 | Executor PR template inline | ✅ |
|
||||
| 10 | Executor v2 prompt | ✅ written, not yet run end-to-end |
|
||||
|
||||
## Executor v2 architecture (Phase 4 plan)
|
||||
|
||||
Autonomous worker: evaluates backlog → picks task → spawns sub-agent in Discord thread under `#executor` → streams progress → produces draft PR → self-reviews → CI loop → surfaces in daily/weekly briefs.
|
||||
|
||||
**Triggers:** user-initiated (`fix [task]` in `#executor`), scheduled (Friday weekly-review queue), status-driven (Eagle reads `## ⚡ Executor Queue` from status.md).
|
||||
|
||||
**Qualification SQL:** assigned to Alex, not completed, no stories in 14+ days, due within 30 days.
|
||||
|
||||
**Worker flow:** Eagle creates Discord thread → spawns sub-agent (`--worktree` mode, workdir `~/DuckDuckGo/apple-browsers.git/.claude/worktrees/executor-<gid>-<slug>/`) → posts streaming updates per phase → opens PR → self-review comment → polls `gh pr checks` every 10min → on red, auto-fix max 3 attempts then escalate.
|
||||
|
||||
**Phases:** 4a evaluation engine · 4b worker spawning + threads · 4c PR + CI loop · 4d feedback loop on PR comments · 4e scheduled evaluation.
|
||||
|
||||
## Key decisions (divergence from earlier plan v3)
|
||||
|
||||
1. **wiki-ingest** moved from Hermes cron → launchd `claude -p` subprocess. Filesystem I/O requires local process; Hermes API agents have no filesystem.
|
||||
2. **personal-wiki MCP** dropped, replaced with stock `obsidian-mcp` against `~/obsidian/`. The DB-backed wiki was never built; vault is the source of truth now.
|
||||
3. **Single heartbeat watchdog** instead of per-job launchd plists — simpler, covers wake-from-sleep for all one-per-day jobs.
|
||||
4. **Retrospector** extracted to standalone `retrospector.md` + own cron + `#retrospector` channel, separating "what moved" (review) from "what's the pattern" (retro).
|
||||
5. **NAS git path** moved to `/mnt/RED_2TB/storage/git/` — `/home/` lives on TrueNAS boot pool which is wiped on OS updates.
|
||||
6. **Phase 2-family deferred** until after Phase 4 — Executor v2 has higher leverage than family infra.
|
||||
7. **Executor v1 manual-only** with explicit STOP gates — first autonomous code-changing agent stays conservative.
|
||||
8. **wiki-ingest prompt** rewritten DB-first → file-based with SHA256 frontmatter `content_hash`. See [[wiki-ingest-process]].
|
||||
|
||||
## Open questions before Executor v2 starts (all resolved)
|
||||
|
||||
- Repo: `~/DuckDuckGo/apple-browsers.git/` bare repo, sibling worktrees, main at `main/`
|
||||
- Worktree path: `.claude/worktrees/executor-{gid}-{slug}/`
|
||||
- Xcode scheme: `DuckDuckGo macOS`, `.xcworkspace` required, xcbeautify required
|
||||
- PR template: `pull-request.mdc` strict gates overridden by Executor v2 (task known, reviewer=@me, description auto-generated)
|
||||
- Cleanup: `git worktree remove --force` + `git push --delete` + stop/delete VM
|
||||
- UI test VM: ddg-vm MCP at `~/Developer/virfield/server/mcp-server.ts`, VirtualBuddy at `~/Documents/VirtualBuddy/`
|
||||
|
||||
## Tech debt
|
||||
|
||||
- Morning brief never smoke-tested (first real run = next 08:30) — Medium
|
||||
- `work/projects/` empty (Eagle fills on first relevant conversation) — Low
|
||||
- `executor-bug-fix.md` v1 still manual-only — replaced by v2 architecture above — Medium
|
||||
- wiki-ingest `claude -p` runs without `--allowedTools` — Low
|
||||
- **NAS SSH key in boot-pool `/home/`** — High; `authorized_keys` will be wiped on TrueNAS update; move under `/mnt/RED_2TB/`
|
||||
- Hermes memory `~/.hermes/memory/` not backed up — Low
|
||||
|
||||
## Deferred — Phase 2-family
|
||||
|
||||
VPS (Ubuntu 24.04 $5/mo) `family_db` postgres + family Hermes instance + iCloud vault sync (`~/Library/Mobile Documents/iCloud~md~obsidian/`) for wife's iPhone Obsidian + NAS hourly rsync. Resumes after Phase 4.
|
||||
|
||||
## Related
|
||||
|
||||
[[personal-os-catchup-plan-2026-04-27]] [[personal-os-architecture]] [[personal-os-schema]] [[personal-os-sync-pipeline]] [[personal-os-agent-rules]] [[personal-os-self-modification]] [[personal-os-purpose]] [[wiki-ingest-process]] [[ddg-asana-workflow]] [[vault-filling-guide]]
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
namespace: work
|
||||
tags: [system, sync, pipeline, debugging]
|
||||
last_synced: 2026-04-27
|
||||
confidence: 1.0
|
||||
---
|
||||
|
||||
# Personal OS — Sync Pipeline
|
||||
|
||||
## Components
|
||||
|
||||
### sync.js
|
||||
Pulls from 4 Asana sources into PostgreSQL. Run: `node ~/Developer/personal-os/sync.js`
|
||||
|
||||
**Sources fetched:**
|
||||
1. `fetchMyTasks()` — My Tasks sections by GID (section-by-section)
|
||||
2. `fetchFollowingTasks()` — tasks I follow, 7-day chunks to work around 100-result cap
|
||||
3. `fetchDelegatedTasks()` — created by me, assigned to others
|
||||
4. `fetchProjectTasks()` — tasks from projects I'm a member of (lookback window on full sync)
|
||||
5. `sweepRecentlyCompleted()` — catch completed tasks missed by above
|
||||
|
||||
**Incremental sync**: uses `modified_since` cursor from `sync_state` table.
|
||||
**Full sync**: triggered when `full_sync_at` is older than configured threshold.
|
||||
|
||||
### generate-status.js
|
||||
Reads from PostgreSQL → writes `asana_context.md` + `status.md`.
|
||||
Run: `node ~/Developer/personal-os/generate-status.js`
|
||||
|
||||
### run-pipeline.sh
|
||||
Wrapper: runs sync.js + generate-status.js + healthcheck.
|
||||
`~/scripts/run-pipeline.sh`
|
||||
|
||||
Healthcheck: `asana_context.md` must be < 40 minutes old. Exits 1 if stale.
|
||||
|
||||
## Known Bugs (Fixed)
|
||||
|
||||
### possibly_deleted + overdue tasks appearing
|
||||
**Root cause**: Full sync marks tasks absent from Asana response as `possibly_deleted=true`.
|
||||
These tasks remained `completed=false`, causing them to appear overdue.
|
||||
|
||||
**Fix 1 — generate-status.js**: Added `AND t.possibly_deleted = FALSE` to all 5 active-task queries.
|
||||
|
||||
**Fix 2 — sync.js**: Added `sweepRecentlyCompleted()` called after `syncMyTasks()`.
|
||||
Uses `searchTasksForWorkspace(completed:true, completed_at.after=cursor)` to catch tasks
|
||||
that vanished from sections (because completed tasks are excluded from section membership results).
|
||||
|
||||
### Completion gap
|
||||
`getTasksForSection` drops completed tasks. `fetchFollowingTasks` has `completed:false` filter.
|
||||
→ Completion events never reach DB incrementally.
|
||||
→ `sweepRecentlyCompleted()` fills this gap.
|
||||
|
||||
## Debugging Checklist
|
||||
|
||||
**Pipeline seems stuck:**
|
||||
```bash
|
||||
bash ~/scripts/run-pipeline.sh
|
||||
# Check exit code and output
|
||||
```
|
||||
|
||||
**Check when last sync ran:**
|
||||
```sql
|
||||
SELECT key, last_sync_at, full_sync_at FROM sync_state ORDER BY last_sync_at DESC;
|
||||
```
|
||||
|
||||
**Check possibly_deleted tasks (should be 0 active):**
|
||||
```sql
|
||||
SELECT count(*) FROM tasks WHERE possibly_deleted=true AND completed=false;
|
||||
```
|
||||
|
||||
**Check asana_context.md freshness:**
|
||||
```bash
|
||||
stat -f "%Sm" ~/Developer/personal-os/asana_context.md
|
||||
```
|
||||
|
||||
**Manual full sync (reset cursor):**
|
||||
```sql
|
||||
UPDATE sync_state SET cursor = null, full_sync_at = null WHERE key = 'my_tasks';
|
||||
```
|
||||
Then run `node sync.js`.
|
||||
|
||||
## Files
|
||||
|
||||
- `~/Developer/personal-os/sync.js` — main sync script
|
||||
- `~/Developer/personal-os/generate-status.js` — status generation
|
||||
- `~/Developer/personal-os/asana.js` — thin Asana API client
|
||||
- `~/Developer/personal-os/config.json` — workspace GID, user GID, section GIDs
|
||||
- `~/.config/personal-os/env` — ASANA_API_KEY, POSTGRES_URL
|
||||
|
||||
## Related
|
||||
|
||||
[[personal-os-architecture]] [[personal-os-agent-rules]] [[ddg-asana-workflow]]
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title: Research Queue
|
||||
updated: '2026-05-24'
|
||||
type: meta
|
||||
---
|
||||
|
||||
# Research Queue
|
||||
|
||||
> Topics detected as gaps in the wiki — mentioned in vault but no wiki page.
|
||||
> Ordered by priority. Completed items move to ## Completed with link + date.
|
||||
|
||||
## Queue
|
||||
|
||||
(empty)
|
||||
|
||||
## Completed
|
||||
|
||||
- [x] WireGuard VPN → [[tech/wireguard-vpn]] (2026-05-23)
|
||||
- [x] Executor Architecture v2 → [[concepts/executor-orchestrator]] (2026-05-19)
|
||||
- [x] Executor Security Incident → [[concepts/executor-security-incident]] (2026-05-22)
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
title: Vault ↔ Agent Integration
|
||||
created: '2026-05-23'
|
||||
updated: '2026-05-23'
|
||||
type: concept
|
||||
namespace: work
|
||||
tags: [vault, obsidian, wiki, ingest, mcp, hermes, system, architecture]
|
||||
sources: [wiki/obsidian-mcp-wrapper.md, wiki/wiki-ingest-process.md, wiki/personal-os-architecture.md, wiki/vault-filling-guide.md]
|
||||
confidence: high
|
||||
---
|
||||
|
||||
# Vault ↔ Agent Integration
|
||||
|
||||
How Eagle, wiki-ingest, and the obsidian-mcp-wrapper work together as a
|
||||
unified knowledge layer. Three distinct subsystems each own a slice of
|
||||
the vault.
|
||||
|
||||
## The Three Subsystems
|
||||
|
||||
### 1. obsidian-mcp-wrapper (runtime read/write)
|
||||
|
||||
A Node.js proxy (`~/scripts/obsidian-mcp-wrapper.js`) wraps `obsidian-mcp`
|
||||
to fix four production bugs: ZodError on init, race condition at gateway
|
||||
restart, UTF-8 chunk corruption on large payloads, and a 5-second watchdog
|
||||
for hung child processes.
|
||||
|
||||
Eagle reads the vault through this wrapper via MCP tool calls. It can search,
|
||||
read, and write notes in real time during conversations.
|
||||
|
||||
**Allowed write paths** (Eagle only, with prior draft shown):
|
||||
- `work/projects/`, `work/decisions/`, `personal/`, `family/`
|
||||
- Never `wiki/` — that's wiki-ingest territory
|
||||
|
||||
### 2. wiki-ingest (nightly synthesis, 22:00 launchd)
|
||||
|
||||
A `claude -p` session (not Hermes) that runs nightly. It reads files in
|
||||
`~/obsidian/raw/` (symlinks to external project files), computes SHA256
|
||||
hashes, and synthesises wiki pages only when the source has changed.
|
||||
|
||||
**Invariants:**
|
||||
- Output is synthesis, never verbatim copy
|
||||
- Frontmatter `source` + `content_hash` are the ingestion contract
|
||||
- Pages with `confidence: 1.0` are immutable
|
||||
|
||||
### 3. wiki-curation (daily Hermes cron, 02:00)
|
||||
|
||||
A Hermes cron job (this script) that maintains the wiki as a compounding
|
||||
knowledge base: processes inbox, crystallises session knowledge, creates
|
||||
synthesis pages, lints orphans and broken links.
|
||||
|
||||
## Division of Labour
|
||||
|
||||
| Layer | Tool | Vault path | Trigger |
|
||||
|-------|------|-----------|---------|
|
||||
| Reactive reads/writes | obsidian-mcp-wrapper | `work/`, `personal/`, `family/` | On-demand |
|
||||
| Source synthesis | wiki-ingest (claude -p) | `wiki/` ← `raw/` | 22:00 nightly |
|
||||
| Knowledge curation | wiki-curation (Hermes cron) | `wiki/` | 02:00 daily |
|
||||
| Vault sync | sync-vault.sh | entire vault | Hourly |
|
||||
|
||||
## Why obsidian-mcp Is Wrapped
|
||||
|
||||
The wrapper exists because `obsidian-mcp` v1.0.6 had four production-breaking
|
||||
bugs that would have been too fragile to fix upstream (updates would reintroduce
|
||||
them). A proxy wrapper is cleaner: it intercepts the MCP protocol stream without
|
||||
modifying the underlying package.
|
||||
|
||||
See [[obsidian-mcp-wrapper]] for the four bugs and their fixes.
|
||||
|
||||
## Vault Sync (Eagle ↔ Taiga ↔ Kraken)
|
||||
|
||||
All three nodes share a sparse vault via a TrueNAS bare git repo. Eagle has
|
||||
the full vault; Kraken and Taiga have `personal/` and `family/` only (sparse
|
||||
checkout). The `sync-vault.sh` cron runs hourly on all three.
|
||||
|
||||
```
|
||||
Eagle (full) ──push/pull──┐
|
||||
Taiga (sparse) ────────────├── mallexxx.duckdns.org:/mnt/RED_2TB/storage/git/obsidian-vault.git
|
||||
Kraken (sparse) ───────────┘
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
[[obsidian-mcp-wrapper]] [[wiki-ingest-process]] [[vault-filling-guide]]
|
||||
[[personal-os-architecture]]
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
---
|
||||
namespace: work
|
||||
tags: [system, vault, guide]
|
||||
last_synced: 2026-04-27
|
||||
confidence: 1.0
|
||||
---
|
||||
|
||||
# Vault Filling Guide
|
||||
|
||||
How to populate `~/obsidian/`. Eagle can write to any section below except `wiki/` and `raw/`.
|
||||
|
||||
## Directory Map
|
||||
|
||||
```
|
||||
~/obsidian/
|
||||
├── wiki/ ← LLM-generated ONLY (wiki-ingest). Never write here manually.
|
||||
├── raw/ ← Symlinks to external files ONLY. Never write content here.
|
||||
├── work/ ← Alex's work notes. Eagle can write with confirmation.
|
||||
│ ├── projects/ One file per active project.
|
||||
│ ├── decisions/ Architectural/process decisions with date prefix.
|
||||
│ └── tech-design/ References to designs, review notes.
|
||||
├── personal/ ← Alex's personal notes. Eagle can write with confirmation.
|
||||
│ ├── documents/ WHERE docs live (not the docs themselves).
|
||||
│ ├── instructions/ Personal how-tos and procedures.
|
||||
│ └── projects/ Personal side projects.
|
||||
└── family/ ← Household knowledge. Eagle can write with confirmation.
|
||||
├── how-to/ Practical guides (devices, home equipment, procedures).
|
||||
├── documents/ WHERE family documents live.
|
||||
├── contacts/ Emergency contacts, doctors, services.
|
||||
└── schedule/ Recurring schedules, school, events.
|
||||
```
|
||||
|
||||
## File Naming
|
||||
|
||||
- `work/projects/` → `kebab-case-project-name.md` (e.g. `ios-duck-ai-toggle.md`)
|
||||
- `work/decisions/` → `YYYY-MM-topic.md` (e.g. `2026-04-personal-os-arch.md`)
|
||||
- `family/how-to/` → `kebab-case-topic.md` (e.g. `truenas-access.md`, `router-reset.md`)
|
||||
- `family/contacts/` → `emergency.md`, `doctors.md`, `services.md`
|
||||
- `family/documents/` → `apartment.md`, `insurance.md`, `passports.md` (location pointers, not the files)
|
||||
|
||||
## Frontmatter
|
||||
|
||||
Every file should have:
|
||||
```yaml
|
||||
---
|
||||
namespace: work | personal | family
|
||||
tags: [tag1, tag2]
|
||||
created: YYYY-MM-DD
|
||||
updated: YYYY-MM-DD
|
||||
---
|
||||
```
|
||||
|
||||
## What Goes Where
|
||||
|
||||
| Info type | Location |
|
||||
|-----------|----------|
|
||||
| Active work project status, decisions, links | `work/projects/SLUG.md` |
|
||||
| "We decided X because Y" (architectural) | `work/decisions/YYYY-MM-TOPIC.md` |
|
||||
| Home network / NAS / router config | `family/how-to/DEVICE.md` |
|
||||
| Where passport/insurance/lease is stored | `family/documents/TOPIC.md` |
|
||||
| Emergency contacts, doctors | `family/contacts/CATEGORY.md` |
|
||||
| Recurring events, school schedule | `family/schedule/TOPIC.md` |
|
||||
| Personal project notes | `personal/projects/SLUG.md` |
|
||||
| Personal procedures (backups, etc.) | `personal/instructions/TOPIC.md` |
|
||||
|
||||
## Eagle Write Protocol
|
||||
|
||||
1. Show draft content before writing (never silently create)
|
||||
2. Confirm namespace (`work` / `personal` / `family`)
|
||||
3. Write the file
|
||||
4. Run `cd ~/obsidian && git add -A && git commit -m "..."` to save
|
||||
|
||||
## What Eagle Should NOT Do
|
||||
|
||||
- Write anything to `wiki/` — that's wiki-ingest's job
|
||||
- Modify `raw/` — those are symlinks managed manually
|
||||
- Create files without showing the content first
|
||||
- Overwrite existing files without showing a diff
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: Vault Namespace Rules
|
||||
created: '2026-05-23'
|
||||
updated: '2026-05-23'
|
||||
type: tech
|
||||
namespace: personal
|
||||
tags: [vault, meta, conventions]
|
||||
related:
|
||||
- "[[vault-filling-guide]]"
|
||||
---
|
||||
|
||||
# Vault Namespace Rules
|
||||
|
||||
Rules for where notes live in the Obsidian vault. See also [[SCHEMA]] for
|
||||
full frontmatter conventions.
|
||||
|
||||
## Namespace Assignments
|
||||
|
||||
- `personal`: Eagle infra, personal projects, dev tooling
|
||||
- `family`: Kraken media stack, HTPC, Jellyfin, family content
|
||||
- `work`: DuckDuckGo / DDG tasks and tooling
|
||||
|
||||
## Directory Map
|
||||
|
||||
```
|
||||
wiki/concepts/ — how-it-works explanations
|
||||
wiki/tech/ — infra how-tos, tool configs, pitfalls
|
||||
wiki/ideas/ — speculative, not yet decided
|
||||
personal/projects/ — project status & decisions
|
||||
family/projects/ — family-shared project status
|
||||
family/how-to/ — family infra procedures
|
||||
```
|
||||
|
||||
Note: this file was reconstructed from a lost pointer. Extend with actual
|
||||
rules as they emerge.
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
---
|
||||
source: raw/wiki-ingest-prompt.md
|
||||
content_hash: 29f6a0914d428cf62eeb2a832d6b3048edf3e1d16aa7714c09f686377394d128
|
||||
namespace: work
|
||||
last_synced: 2026-04-27
|
||||
confidence: 0.9
|
||||
tags: [wiki, ingest, agent, prompt]
|
||||
---
|
||||
|
||||
# Wiki Ingest Process
|
||||
|
||||
The agent prompt that maintains `~/obsidian/wiki/` as a synthesised
|
||||
knowledge base over external project files exposed via symlinks in
|
||||
`~/obsidian/raw/`. Run locally by `claude -p` (the launchd job
|
||||
`run-wiki-ingest.sh` at 22:00) — not via API, because file-system
|
||||
writes require a local Claude Code session. See
|
||||
[[personal-os-architecture]] for where this fits in the broader system.
|
||||
|
||||
## Discovery loop
|
||||
|
||||
For every file in `~/obsidian/raw/`:
|
||||
|
||||
1. Read the file (skip if the symlink target is missing).
|
||||
2. Compute SHA256 with `shasum -a 256`.
|
||||
3. Look for a wiki page whose frontmatter has `source: raw/<filename>`.
|
||||
4. If a page exists and its `content_hash` already matches, skip.
|
||||
5. Otherwise enqueue for synthesis.
|
||||
|
||||
If nothing changed, the agent prints
|
||||
"Wiki is up to date. Nothing to ingest." and exits.
|
||||
|
||||
## Synthesis rules
|
||||
|
||||
- Wiki pages are named by **topic**, not source filename
|
||||
(e.g. `personal-os-schema.md`, not `schema.md`).
|
||||
- Output is a **synthesis**, never a verbatim copy. Extract facts,
|
||||
decisions, and structure. Hard cap of 800 words per page; split
|
||||
into linked pages if the topic is larger.
|
||||
- Use `[[double brackets]]` for cross-references between wiki pages.
|
||||
|
||||
## Page format
|
||||
|
||||
Frontmatter is the source of truth for incremental ingestion:
|
||||
|
||||
```yaml
|
||||
source: raw/<filename>
|
||||
content_hash: <sha256>
|
||||
namespace: work
|
||||
last_synced: <YYYY-MM-DD>
|
||||
confidence: 0.8
|
||||
tags: [tag1, tag2]
|
||||
```
|
||||
|
||||
Body has a title, synthesis prose, a `## Key Points` list, and a
|
||||
`## Related` line of `[[wiki-links]]`.
|
||||
|
||||
## Confidence ladder
|
||||
|
||||
- **1.0** — reserved for human-written notes in `work/`, `personal/`,
|
||||
`family/`. The agent must never edit these files.
|
||||
- **0.9** — highly structured / authoritative source (e.g. a schema).
|
||||
- **0.8** — clear single source. Default.
|
||||
- **0.6** — inferred or partial content.
|
||||
|
||||
## Hard rules
|
||||
|
||||
- Never copy source files verbatim — always synthesise.
|
||||
- Never edit any file with `confidence: 1.0`.
|
||||
- Never process files in `namespace: family` unless explicitly told.
|
||||
- Always update `content_hash` and `last_synced` after writing a page.
|
||||
- Skip broken symlinks; do not create a wiki page for them.
|
||||
|
||||
## Reporting
|
||||
|
||||
After the run, the agent emits a summary:
|
||||
|
||||
```
|
||||
Wiki Ingest — <date>
|
||||
|
||||
Processed: N files
|
||||
Created: N new pages
|
||||
Updated: N pages
|
||||
Skipped: N (unchanged)
|
||||
|
||||
Pages updated:
|
||||
- wiki/<filename> (source: raw/<source>)
|
||||
```
|
||||
|
||||
## Key Points
|
||||
- Hash-based incremental: re-runs are cheap when nothing changed.
|
||||
- Topic-named pages decouple the wiki from source-file naming.
|
||||
- Frontmatter `source` + `content_hash` is the ingestion contract;
|
||||
the [[personal-os-schema]] mirrors the same idea in `wiki_pages`.
|
||||
- Human-edited (confidence 1.0) pages are immutable to the agent —
|
||||
the trust boundary between synthesis and curated knowledge.
|
||||
- Local-only execution: writes to `~/obsidian/` need a real FS,
|
||||
so the job runs under launchd via `claude -p`, not the API.
|
||||
|
||||
## Related
|
||||
[[personal-os-architecture]] [[personal-os-schema]]
|
||||
Executable
+125
@@ -0,0 +1,125 @@
|
||||
---
|
||||
title: Wiki Schema
|
||||
created: '2026-05-13'
|
||||
updated: '2026-05-25'
|
||||
type: meta
|
||||
---
|
||||
|
||||
# Wiki Schema
|
||||
|
||||
## Domain
|
||||
|
||||
Knowledge base covering Alex's Personal OS ecosystem: the multi-agent
|
||||
productivity/context system, home infrastructure (Eagle Mac M4, Taiga TrueNAS,
|
||||
Kraken RPi5), personal projects (Reflect app, etc.), and DuckDuckGo work tooling.
|
||||
|
||||
**In scope:** agent architecture, data pipelines, home infra, dev tooling,
|
||||
personal projects, vault management.
|
||||
**Out of scope:** raw Asana tasks, family documents, ephemeral briefs.
|
||||
|
||||
## Conventions
|
||||
|
||||
- File names: lowercase, hyphens, no spaces (e.g., `personal-os-architecture.md`)
|
||||
- Every wiki page starts with YAML frontmatter (see below)
|
||||
- Use `[[wikilinks]]` to link between pages (minimum 2 outbound links per page)
|
||||
- When updating a page, always bump the `updated` or `last_synced` date
|
||||
- Every new page must be added to `index.md` under the correct section
|
||||
- Every action must be appended to `log.md`
|
||||
- Subdirectories: `concepts/` for concept/how-it-works pages,
|
||||
`tech/` for infra/tool how-tos, `comparisons/` for side-by-side analyses,
|
||||
`entities/` for people/orgs/projects, `queries/` for filed query results
|
||||
|
||||
## Frontmatter
|
||||
|
||||
Wiki pages synthesised from `raw/`:
|
||||
```yaml
|
||||
---
|
||||
source: raw/<filename>
|
||||
content_hash: <sha256>
|
||||
namespace: work | personal | family
|
||||
last_synced: YYYY-MM-DD
|
||||
confidence: 0.8
|
||||
tags: [tag1, tag2]
|
||||
---
|
||||
```
|
||||
|
||||
Hand-written or agent-synthesised pages:
|
||||
```yaml
|
||||
---
|
||||
title: Human Readable Title
|
||||
created: YYYY-MM-DD
|
||||
updated: YYYY-MM-DD
|
||||
type: concept | entity | comparison | tech | query
|
||||
namespace: work | personal | family
|
||||
tags: [from taxonomy below]
|
||||
sources: [raw/articles/source.md]
|
||||
confidence: high | medium | low
|
||||
---
|
||||
```
|
||||
|
||||
**Confidence ladder:**
|
||||
- `1.0` / `high` — human-written; agent must never edit these files
|
||||
- `0.9` — highly structured / authoritative source (schema, infra docs)
|
||||
- `0.8` / `medium` — single clear source; default for agent synthesis
|
||||
- `0.6` / `low` — inferred or partial content
|
||||
|
||||
Pages with `confidence: 1.0` are immutable to the wiki-curation agent.
|
||||
|
||||
## Tag Taxonomy
|
||||
|
||||
Personal OS:
|
||||
- `system` `architecture` `agent` `eagle` `executor` `pipeline` `sync`
|
||||
- `rules` `self-modification` `meta` `purpose`
|
||||
|
||||
Work / DDG:
|
||||
- `asana` `ddg` `workflow` `task-management`
|
||||
|
||||
Infrastructure:
|
||||
- `infra` `kraken` `taiga` `htpc` `docker` `nas` `raspberry-pi` `vps`
|
||||
- `obsidian` `vault` `wiki` `ingest`
|
||||
- `media-pipeline` `arr`
|
||||
|
||||
Projects:
|
||||
- `reflect-app` `psychology` `ios` `android` `skip-tools`
|
||||
- `project` `startup`
|
||||
|
||||
Technical:
|
||||
- `schema` `postgres` `database` `mcp` `hermes`
|
||||
- `debugging` `pitfalls` `how-to`
|
||||
|
||||
Rule: every tag on a page must appear in this taxonomy. Add new tags HERE
|
||||
before using them. Avoid tag sprawl.
|
||||
|
||||
## Page Thresholds
|
||||
|
||||
- **Create a page** when an entity/concept appears in 2+ sources OR is
|
||||
central to one source
|
||||
- **Add to existing page** when a source mentions something already covered
|
||||
- **DON'T create a page** for passing mentions or things outside the domain
|
||||
- **Split a page** when it exceeds ~200 lines
|
||||
- **Never edit** pages with `confidence: 1.0`
|
||||
|
||||
## Update Policy
|
||||
|
||||
When new information conflicts with existing content:
|
||||
1. Check dates — newer sources generally supersede older ones
|
||||
2. If genuinely contradictory, note both positions with dates
|
||||
3. Never silently overwrite — show what changed
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
wiki/
|
||||
├── SCHEMA.md ← this file
|
||||
├── index.md ← page catalog
|
||||
├── log.md ← action log (append-only)
|
||||
├── *.md ← root-level pages (legacy + core)
|
||||
├── concepts/ ← how-it-works, design rationale
|
||||
├── tech/ ← infra/tool how-tos (device-specific)
|
||||
├── comparisons/ ← side-by-side analyses
|
||||
├── entities/ ← people, projects, products
|
||||
├── queries/ ← filed query results
|
||||
└── raw/ ← immutable sources
|
||||
├── inbox/ ← new files to process
|
||||
└── inbox/processed/ ← after ingestion
|
||||
```
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: Reflect — Skip Fuse (SwiftUI/Kotlin)
|
||||
created: '2026-05-23'
|
||||
updated: '2026-05-23'
|
||||
type: tech
|
||||
namespace: personal
|
||||
tags: [reflect, swift, kotlin, ios, android, pitfalls]
|
||||
related:
|
||||
- "[[entities/psychologist-app]]"
|
||||
- "[[tech/gitea-config]]"
|
||||
---
|
||||
|
||||
# Reflect — Skip Fuse (SwiftUI/Kotlin)
|
||||
|
||||
## Key Pitfall
|
||||
|
||||
When adding a **new SwiftUI View** to the Reflect iOS codebase, Skip Fuse
|
||||
does **not** auto-generate the Kotlin counterpart. A manual Kotlin stub is
|
||||
required in **2 places**:
|
||||
|
||||
1. The View class file in the Android module
|
||||
2. The registration/factory in the Android navigation/router
|
||||
|
||||
## Why
|
||||
|
||||
Skip Fuse transpiles shared SwiftUI code to Kotlin, but new View types
|
||||
require explicit Kotlin stubs until Skip's transpilation coverage catches up.
|
||||
|
||||
## Checklist for New SwiftUI Views
|
||||
|
||||
- [ ] Add SwiftUI view as normal in iOS target
|
||||
- [ ] Create matching Kotlin stub in `android/src/.../views/`
|
||||
- [ ] Register in Android router/factory
|
||||
- [ ] Run `./gradlew build` to verify no missing class errors
|
||||
Reference in New Issue
Block a user