diff --git a/family/how-to/arr-stack-kraken.md b/family/how-to/arr-stack-kraken.md new file mode 100755 index 00000000..2fa383cd --- /dev/null +++ b/family/how-to/arr-stack-kraken.md @@ -0,0 +1,136 @@ +--- +title: Arr Stack — Kraken +created: '2026-05-23' +updated: '2026-05-27' +type: tech +namespace: family +tags: [arr, radarr, sonarr, prowlarr, transmission, jellyfin, infra, kraken] +related: + - "[[tech/jellyfin-config]]" + - "[[tech/arr-stack-taiga]]" + - "[[concepts/kraken-media-stack]]" +--- + +# Arr Stack — Kraken + +## Pipeline + +``` +Prowlarr → Radarr + Sonarr → Transmission → Jellyfin + ↓ + router.py (cron 10min) + ↓ + symlinks → /media/{movies,cartoons,series,documentaries,...}/ +``` + +Два параллельных пути поступления контента: + +1. **Radarr/Sonarr** → качают в `/media/movies-radarr/` и `/media/series-sonarr/` +2. **Transmission (ручные торренты)** → `on-download-complete.sh` → `sync.py` → `/media/{movies,cartoons,...}/` + +### router.py — жанровая маршрутизация + +`router.py` читает NFO-файлы (которые Radarr/Sonarr создают при загрузке), определяет жанр и возрастной рейтинг, и создаёт symlink в нужную Jellyfin-библиотеку: + +| Источник | Условие | Цель | +|---|---|---| +| movies-radarr/ | Documentary | `/media/documentaries/` | +| movies-radarr/ | Animation + !R/18+ | `/media/cartoons/` | +| movies-radarr/ | всё остальное | `/media/movies/` | +| series-sonarr/ | Documentary | `/media/documentaries-series/` | +| series-sonarr/ | Animation + !TV-MA | `/media/cartoons-series/` | +| series-sonarr/ | всё остальное | `/media/series/` | + +**Расположение:** `/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker/media-pipeline/router.py` +**Запуск:** `python3 router.py --dry-run` (проверка) / `python3 router.py --apply` (исполнение) + +### media-router.sh — cron-скрипт для Telegram-уведомлений + +Обёртка над `router.py`, которая: +- запускает `router.py --apply` +- если `Nothing to do` — **SILENT**, ничего не шлёт +- если созданы symlink'и или ошибка — отправляет отчёт в Telegram Kraken + +**Расположение:** рядом с `router.py` — `media-router.sh` +**Cron (Kraken crontab):** каждые 10 минут +**Telegram:** kraken_htpc_bot → чат @mallex87 + +### sync.py — обработка ручных торрентов (Transmission) + +`sync.py` работает внутри Transmission-контейнера. При завершении загрузки: +1. Определяет название через TMDB API +2. Определяет жанр +3. Перемещает файл в `/media/{movies,cartoons,series,documentaries}/` +4. Обновляет `downloadDir` в Transmission через RPC + +**Расположение:** `/srv/.../docker/media-pipeline/sync.py` +**Триггер:** `on-download-complete.sh` (смонтирован в Transmission как `/config/on-download-complete.sh:ro`) + +## Transmission + +- **download-dir:** `/downloads` (без `complete`) +- **incomplete-dir:** `/downloads/incomplete` +- **Язык:** отключён auth для локальных +- **RPC:** `localhost:9091` + +При перемещении файлов для уже завершённых торрентов: +1. Переименовать файл/папку под кейс торрента (Linux ext4 case-sensitive) +2. Вызвать `torrent-set-location` с `move=false` +3. Запустить торрент на сидирование + +## Cron'ы на Kraken (crontab) + +``` +0 * * * * bash ~/scripts/sync-vault.sh # Obsidian sync +0 * * * * pgs-to-srt.sh /media/downloads # PGS→SRT новые +0 3 * * * pgs-to-srt.sh /media # PGS→SRT вся библиотека +0 1 * * * bash ~/scripts/watchlist-nightly.sh # watchlist nightly +0 9 * * 0 bash ~/scripts/watchlist-discover.sh # watchlist discover +*/10 * * * * bash .../media-pipeline/media-router.sh # media router (Telegram) +``` + +## API Keys + +| Service | API Key | +|-------------|----------------------| +| Radarr | `cbcb8ec3...` | +| Sonarr | `15fbec32...` | +| Prowlarr | `134ac38a...` | + +(Full keys truncated — retrieve from each service's settings page.) + +## Ports (default Docker network) + +All services accessible at `kraken:`: + +| Service | Port | +|--------------|------| +| Radarr | 7878 | +| Sonarr | 8989 | +| Prowlarr | 9696 | +| Transmission | 9091 | +| Jellyfin | 8096 | + +## Jellyfin библиотеки + +| Библиотека | Тип | Путь | +|---|---|---| +| Movies | movies | `/media/movies` | +| Series | tvshows | `/media/series` | +| Cartoons | movies | `/media/cartoons` | +| Cartoon Series | tvshows | `/media/cartoons-series` | +| Documentaries | movies | `/media/documentaries` | +| Documentary Series | tvshows | `/media/documentaries-series` | + +> `movies-radarr` и `series-sonarr` НЕ добавлены в Jellyfin — router.py создаёт symlink'и из них в перечисленные пути. + +## Известные проблемы + +- **17 фильмов в Radarr `hasFile=False`** — ждут подходящего релиза в Prowlarr. Scream (1996) в процессе загрузки. +- **Prowlarr** — если долго не находит релизы, проверить статус индексеров (`/api/v1/health`). + +## Notes + +- See [[arr-stack-taiga]] for Taiga (secondary) arr stack +- Media pipeline pitfalls: [[media-pipeline-pitfalls]] +- Синтез: [[concepts/kraken-media-stack]] diff --git a/family/how-to/arr-stack-taiga.md b/family/how-to/arr-stack-taiga.md new file mode 100755 index 00000000..8064641a --- /dev/null +++ b/family/how-to/arr-stack-taiga.md @@ -0,0 +1,31 @@ +--- +title: Arr Stack — Taiga +created: '2026-05-23' +updated: '2026-05-23' +type: tech +namespace: family +tags: [arr, taiga, infra, media, pitfalls] +related: + - "[[tech/arr-stack-kraken]]" +--- + +# Arr Stack — Taiga + +Secondary arr stack on Taiga (TrueNAS), added 2026-05-20. + +## Notes from Setup (2026-05-20) + +- Config and pitfalls recorded during initial Taiga arr stack deployment +- `router.py` was customized — check `/mnt/RED_2TB/docker/arr-taiga/router.py` + for current state + +## Key Differences from Kraken Stack + +- Taiga = TrueNAS host (storage-focused) +- Kraken = Raspberry Pi 5 (playback-focused) +- Taiga stack handles acquisition; Kraken handles serving to Jellyfin + +## Pitfalls + +(Details were referenced but not captured. Update this page after next +Taiga arr maintenance session.) diff --git a/family/how-to/gitea-config.md b/family/how-to/gitea-config.md new file mode 100755 index 00000000..cd73c2d4 --- /dev/null +++ b/family/how-to/gitea-config.md @@ -0,0 +1,27 @@ +--- +title: Gitea Config +created: '2026-05-23' +updated: '2026-05-23' +type: tech +namespace: personal +tags: [infra, gitea, git, credentials] +related: + - "[[tech/kraken-network]]" +--- + +# Gitea Config + +**URL:** `http://git.mallexxx.duckdns.org` (external) / `http://172.16.3.4:3000` (internal LAN) +**User:** `git_admin` + +## API Token + +Token name: `eagle-reflect` +Token value: `f2e679a571f4c599d64b384a711043f995fd8844` + +Used by Eagle for repo operations (push, PR creation, hook management). + +## Notes + +- External URL uses DuckDNS DDNS pointing to home IP +- Internal API endpoint preferred when on home network diff --git a/family/how-to/hermes-docker-kraken.md b/family/how-to/hermes-docker-kraken.md new file mode 100755 index 00000000..a8069da1 --- /dev/null +++ b/family/how-to/hermes-docker-kraken.md @@ -0,0 +1,37 @@ +--- +title: Hermes Docker on Kraken +created: '2026-05-23' +updated: '2026-05-29' +type: tech +namespace: personal +tags: [hermes, docker, kraken, infra, how-to] +related: + - "[[tech/kraken-network]]" + - "[[concepts/hermes-deployment-patterns]]" + - "[[tech/hermes-kraken-api]]" +--- + +# Hermes Docker on Kraken + +## Key Rule + +Hermes runs via **`docker run`**, NOT `docker compose`. No compose file. + +## Launch Pattern + +```bash +docker run -d \ + --name hermes \ + --restart unless-stopped \ + -v ~/.hermes:/root/.hermes \ + ... \ + hermes-image:tag +``` + +(Add actual flags from the running container: `docker inspect hermes`) + +## Notes + +- Using `docker run` keeps restart behavior explicit +- No compose means no accidental `docker compose down` wipes it +- Config/memory volume: `~/.hermes` on Kraken host diff --git a/family/how-to/hermes-eagle-mac.md b/family/how-to/hermes-eagle-mac.md new file mode 100755 index 00000000..ad8f6edf --- /dev/null +++ b/family/how-to/hermes-eagle-mac.md @@ -0,0 +1,229 @@ +--- +title: Hermes на Eagle (Mac M4 Max) — Настройка и подводные камни +type: reference +namespace: work +tags: + - hermes + - mac + - eagle + - claude-proxy + - zulip + - pitfalls +created: '2026-05-21' +updated: '2026-05-22' +last_synced: '2026-05-22' +confidence: 0.9 +--- +# Hermes на Eagle (Mac M4 Max) — Настройка и подводные камни + +Hermes работает нативно (не в Docker) на Mac через `hermes gateway`. +Транспорт — Zulip (запущен в Docker). Провайдер модели — openclaw-claude-proxy +(см. ниже). + +## Компоненты + +| Компонент | Расположение | Запуск | +|-----------|-------------|--------| +| Hermes config | `~/.hermes/config.yaml` | — | +| claude-proxy (Claude proxy) | `/opt/homebrew/bin/claude-proxy` | launchd `ai.claude-proxy` | +| Zulip stack | `~/Developer/zulip/docker-compose.yml` | `docker compose up -d` | +| Obsidian MCP | mcpvault | встроен в Hermes toolset | + +--- + +## openclaw-claude-proxy — обход rate limit Claude API + +### Проблема + +`provider: claude-code` в Hermes использует OAuth-токен напрямую через +API Anthropic — и упирается в rate limit подписки. Лимиты сбрасываются +раз в час. API-ключа нет (политика организации). + +### Почему не cmappy + +cmappy (`claude-max-proxy-py`) молча выбрасывает поле `tools` из запроса — +передаёт только текст в `claude --print`. Результат: Hermes не может +использовать **ни один инструмент** (скиллы, MCP, терминал). Только голый +чат. + +### Решение: openclaw-claude-proxy + +[mehdic/claude-proxy](https://github.com/mehdic/claude-proxy) (npm: +`openclaw-claude-proxy`) — Node.js сервер, запускает `claude --print` как +subprocess и предоставляет OpenAI-совместимый `/v1/chat/completions` на +порту 3456. **Поддерживает tool_use** — инжектирует схемы инструментов в +системный промпт, парсит JSON tool_call из ответа, возвращает стандартный +OpenAI `tool_calls`. Caller (Hermes) сам выполняет инструменты. + +**Важно:** `CLAUDE_PROXY_TOOLS_TRANSLATION=1` НЕ включать — этот режим +выполняет MCP инструменты внутри CLI и Hermes ничего не получает. + +### Установка + +```bash +npm install -g openclaw-claude-proxy +``` + +### Wrapper-скрипт (обязателен для launchd) + +`~/.local/bin/claude-proxy-start.sh`: +```bash +#!/bin/zsh +# launchd не наследует среду login-сессии — токен нужно загружать явно +set -a +source /Users/admin/.hermes/.env 2>/dev/null +set +a +export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:$PATH" +exec /opt/homebrew/bin/claude-proxy 3456 +``` + +**Pitfall:** без явного `source ~/.hermes/.env` claude-proxy не видит +`CLAUDE_CODE_OAUTH_TOKEN` и прогревочные процессы падают с "Not logged in". + +**Pitfall:** без явного PATH Claude CLI не найден (`/opt/homebrew/bin/claude` +не в launchd PATH). + +### launchd сервис + +`~/Library/LaunchAgents/ai.claude-proxy.plist`: +```xml + + + + + Labelai.claude-proxy + ProgramArguments + + /Users/admin/.local/bin/claude-proxy-start.sh + + EnvironmentVariables + + HOME/Users/admin + + RunAtLoad + KeepAlive + StandardOutPath + /Users/admin/.hermes/logs/claude-proxy.log + StandardErrorPath + /Users/admin/.hermes/logs/claude-proxy.log + + +``` + +```bash +launchctl load ~/Library/LaunchAgents/ai.claude-proxy.plist +``` + +**Pitfall при перезагрузке:** если старый процесс ещё держит порт 3456: +```bash +lsof -ti :3456 | xargs kill -9 +launchctl unload ~/Library/LaunchAgents/ai.claude-proxy.plist +launchctl load ~/Library/LaunchAgents/ai.claude-proxy.plist +``` + +### Конфигурация Hermes + +`~/.hermes/config.yaml` (секция model): +```yaml +model: + default: claude-sonnet-4-6 + # provider: claude-code # отключён — упирается в rate limit OAuth API + provider: custom + base_url: 'http://localhost:3456/v1' +``` + +**Pitfall:** `provider: openai` не существует в Hermes — нужно `custom`. + +**Pitfall:** `base_url` должен включать `/v1` (Hermes дописывает +`/chat/completions`). Без `/v1` → 404. + +--- + +## Zulip Docker — подводные камни + +### RabbitMQ: пользователи сбрасываются после перезапуска + +**Симптом:** Zulip отдаёт 500 на `/api/v1/register`. В логах RabbitMQ — +паника Khepri (Raft WAL). Пользователи в RabbitMQ исчезают. + +**Причина:** RabbitMQ 4.x использует Khepri вместо Mnesia. При переполнении +диска WAL не может записаться → Khepri сбрасывает состояние → пользователи +исчезают. `RABBITMQ_DEFAULT_USER/PASS` применяются только при **первом +старте** с пустым volume — повторный запуск их не восстанавливает. + +**Решение:** + +1. `docker system prune` — освободить место на диске (Docker VM sparse disk + не освобождает место автоматически). +2. Добавить `RABBITMQ_ERLANG_COOKIE` в env rabbitmq (стабилизирует cookie + через перезапуски). +3. При повреждённом volume — стереть и пересоздать: + ```bash + docker compose down + docker volume rm zulip_zulip-rabbitmq + docker compose up -d + ``` + +### Log rotation (обязательно!) + +Без ротации логи заполняют Docker VM (~6 ГБ за несколько месяцев). + +`docker-compose.yml` — добавить к каждому сервису: +```yaml +# zulip: +logging: + driver: json-file + options: + max-size: "50m" + max-file: "5" + +# rabbitmq, memcached, redis: +logging: + driver: json-file + options: + max-size: "20m" + max-file: "3" +``` + +### Docker VM sparse disk + +Mac Docker Desktop использует sparse virtual disk. Место, освобождённое +внутри VM, не возвращается хосту автоматически. `docker system prune` +запускает compaction. + +--- + +## Obsidian MCP + +Везде используется `mcpvault` (не `obsidian-mcp`). + +| Конфиг | Путь | +|--------|------| +| Claude Code CLI | `~/.claude/.mcp.json` | +| Claude Desktop App | `~/Library/Application Support/Claude/claude_desktop_config.json` | +| Hermes | встроен через toolset | + +Пример конфига (одинаковый для обоих): +```json +{ + "mcpServers": { + "obsidian": { + "command": "mcpvault", + "args": ["/Users/admin/obsidian"] + } + } +} +``` + +**Pitfall:** `obsidian-mcp` (npm) был удалён — если остался в конфиге, +Claude падает с "Failed to spawn process". Проверить логи: +`~/Library/Logs/Claude/mcp-server-obsidian.log`. + +--- + +## Связанные страницы + +- [[tech/hermes-docker-kraken]] — Hermes на Кракене (Docker) +- [[tech/kraken-network]] — сетевая топология +- [[concepts/hermes-deployment-patterns]] — сравнение трёх моделей деплоя Hermes diff --git a/family/how-to/hermes-kraken-api.md b/family/how-to/hermes-kraken-api.md new file mode 100755 index 00000000..b04d8efe --- /dev/null +++ b/family/how-to/hermes-kraken-api.md @@ -0,0 +1,93 @@ +--- +title: Hermes Kraken — OpenAI-Compatible API Server +created: '2026-05-29' +updated: '2026-05-29' +type: tech +namespace: personal +tags: [hermes, kraken, infra, agent, how-to] +sources: + - family/projects/aide-kraken-backend.md +confidence: medium +related: + - "[[tech/hermes-docker-kraken]]" + - "[[tech/vps-qentra]]" +--- + +# Hermes Kraken — OpenAI-Compatible API Server + +Hermes on Kraken exposes an OpenAI-compatible `/v1/chat/completions` +endpoint (`gateway/platforms/api_server.py`). Exposed externally via +Cloudflare Tunnel → used by the Android **Aide** app as a Hermes-backed +assistant. + +## Architecture + +``` +Android (Aide) + └── HTTPS → hermes.kraken.qentra.top/v1/chat/completions + ↓ Cloudflare Tunnel + cloudflared (Kraken, host network) + ↓ localhost:8642 + hermes-kraken (docker run, network_mode: host) + ↓ + Hermes gateway → Gemini / OpenRouter / ... +``` + +`network_mode: host` means port 8642 is directly on the Kraken host — +no port mapping needed. + +## Config (Kraken /opt/data/config.yaml) + +```yaml +api_server: + enabled: true + port: 8642 + host: "0.0.0.0" + key: "" # openssl rand -hex 32 +``` + +Restart container via Portainer or: +```bash +BASE=http://localhost:9000 +KEY="ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g=" +EP=3 +CID=$(curl -s -H "X-API-Key: $KEY" \ + "$BASE/api/endpoints/$EP/docker/containers/json" \ + | jq -r '.[] | select(.Names[] | contains("hermes-kraken")) | .Id') +curl -s -X POST -H "X-API-Key: $KEY" \ + "$BASE/api/endpoints/$EP/docker/containers/$CID/restart" +``` + +Health check: `curl -s http://localhost:8642/health` + +## Cloudflare Tunnel Setup + +Tunnel name: `kraken`. Public hostname (CF Zero Trust dashboard): +- **Hostname:** `kraken.qentra.top` +- **Type:** HTTP (not SSH) +- **URL:** `localhost:8642` + +SSH to Kraken still works via VPS reverse tunnel (port 2223) — the CF +hostname change does not affect SSH access. + +## Aide Android Client Config + +Provider: Custom Endpoint + +| Field | Value | +|-------|-------| +| Base URL | `https://kraken.qentra.top/v1` | +| API Key | secret from config.yaml | +| Model | `hermes-agent` (maps to Hermes internally) | + +## What Was Not Changed + +- cloudflared container — already running +- VPS reverse SSH tunnel — unaffected +- `network_mode: host` on hermes-kraken — already set + +## See Also + +- [[tech/hermes-docker-kraken]] — how Hermes runs on Kraken (docker run + pattern, no compose) +- [[tech/vps-qentra]] — VPS qentra.top: Cloudflare + nginx stack diff --git a/family/how-to/htpc-bazzite-ecosystem.md b/family/how-to/htpc-bazzite-ecosystem.md new file mode 100755 index 00000000..46b2209a --- /dev/null +++ b/family/how-to/htpc-bazzite-ecosystem.md @@ -0,0 +1,74 @@ +--- +title: HTPC Bazzite Ecosystem — Overview +created: '2026-05-28' +updated: '2026-05-28' +type: concept +namespace: family +tags: [htpc, infra, how-to] +sources: [] +confidence: medium +related: + - "[[htpc-bazzite-proton]]" + - "[[tech/htpc-steam-emulators]]" + - "[[tech/htpc-kodi-layout]]" + - "[[tech/htpc-magic4pc]]" +--- + +# HTPC Bazzite Ecosystem — Overview + +Synthesis across [[htpc-bazzite-proton]], [[tech/htpc-steam-emulators]], +[[tech/htpc-kodi-layout]], and [[tech/htpc-magic4pc]]. + +Intel i7-6800K / RX 570 / Bazzite 44 at `192.168.1.86`. Connected to +LG webOS TV (`192.168.1.75`) via HDMI. + +## Mode Map + +| Mode | Entry | What runs | +|------|-------|-----------| +| Steam Big Picture (gamescope) | Default boot | Steam Gaming Mode + 39 non-Steam shortcuts | +| KDE Desktop | Ctrl+Alt+F2 or Steam → "Switch to Desktop" | Full desktop + Kodi launcher | +| Kodi | From KDE | NFS/SMB media browsing (see [[tech/htpc-kodi-layout]]) | +| Emulators | Steam shortcuts (gamescope or KDE) | RetroArch, EmuDeck | + +## Input Devices + +| Device | Role | +|--------|------| +| 2× 8BitDo Ultimate dongles | Primary gamepads | +| Xbox 360 Wireless Receiver | Secondary gamepad | +| LG Magic Remote (magic4pc) | TV remote → mouse/keyboard via [[tech/htpc-magic4pc]] | + +**8BitDo pitfall:** both dongles must be on separate physical USB ports +(not the same hub). See [[tech/htpc-steam-emulators]]. + +## Windows Games via Proton + +39 games added as non-Steam shortcuts. Most on GE-Proton10-34. +Compatibility matrix and fix recipes: [[htpc-bazzite-proton]]. + +Highlights: +- VK GameCenter (Atomic Heart) — working ✅ +- Rayman Origins — GE-Proton + `d3d9=b` ✅ +- Doom 3 BFG — Luxtorpeda (rbdoom-3-bfg flatpak) ✅ +- 5 games still broken (Brawlhalla, Castle Crashers, Worms ×2, Duck Game) + +## Non-Steam Shortcut Management + +VDF shortcuts via Python `vdf` module. **Rules:** +1. Stop Steam before editing `shortcuts.vdf` +2. Use `vdf.binary_load` — never manual byte editing +3. Keep `bak-eagle` (23 May, 106 entries) as recovery baseline + +See full guide in [[htpc-bazzite-proton]] § VDF. + +## Cover Art + +`steam-grid-artwork.py` (at vault root) — fetches artwork from +SteamGridDB for non-Steam shortcuts. Run when adding new shortcuts. + +## Maintenance Notes + +- `rpm-ostree upgrade` resets `/usr/local/bin/magic4pc` — rebuild and redeploy +- VDF backup: `saves-backup.service` (systemd, daily rsync) +- Proton updates: extract tar.gz into `~/.steam/steam/compatibilitytools.d/` diff --git a/family/how-to/htpc-bazzite-proton.md b/family/how-to/htpc-bazzite-proton.md new file mode 100755 index 00000000..2d1f942e --- /dev/null +++ b/family/how-to/htpc-bazzite-proton.md @@ -0,0 +1,319 @@ +--- +confidence: medium +created: '2026-05-23' +namespace: personal +sources: + - family/projects/htpc-windows-games-fix.md +tags: + - htpc + - infra + - how-to + - pitfalls + - vk-play + - atomic-heart + - vdf + - doom +title: HTPC Bazzite — Proton Compatibility +type: tech +updated: '2026-05-27' +--- +# HTPC Bazzite — Windows Game Proton Compatibility + +Bazzite HTPC at `bazzite@192.168.1.86`. 39 non-Steam Windows games added as +Steam shortcuts (2026-05-22). Default tool: Proton Experimental; older titles +on Proton 8.0. + +--- + +## Working ✅ + +- Fallout 4 +- Vladik Brutal +- Red Alert 3 +- **Rayman Origins** (fixed 2026-05-23 — GE-Proton10-34 + `WINEDLLOVERRIDES=\"d3d9=b\" %command%`) +- Samurai Gunn (works but **gamepad not detected**) +- GTA V, Cuphead (assumed; not retested) +<<<<<<< HEAD +- **VK GameCenter** (GE-Proton10-34, shared prefix c Lutris, авторизация через Lutris + `vk-login-helper`) +- **Atomic Heart VK** (через VK GameCenter, 97GB, `Data/gamecenter/atomic heart/`, URL-схема `vkplay://play/0.2015959`) +======= +- **VK GameCenter** (GE-Proton10-34, `Data/gamecenter/GameCenter/`) +- **Atomic Heart VK** (через VK GameCenter, 97GB, `Data/gamecenter/atomic heart/`) +>>>>>>> nas/main +- **Doom 3 BFG** (через flatpak Classic-RBDOOM-3-BFG) + +## Removed from Steam shortcuts ✅ +- Human Fall Flat (3928445186) — already in real Steam +- Shovel Knight (2526703384) — already in real Steam + +## Mapped to GE-Proton10-34 but still broken + +| Game | AppID | Symptom | LaunchOpt set | +|------|-------|---------|---------------| +| Brawlhalla | 2410005953 | Doesn't start | `WINEDLLOVERRIDES=\"msvcp140,vcruntime140=n,b\"` | +| Castle Crashers | 3048471472 | "crash, mini dump sent" | `WINEDLLOVERRIDES=\"msvcp140,vcruntime140=n,b\"` | +| Worms WMD | 3846411911 | Doesn't start | `WINEDLLOVERRIDES=\"msvcp140,vcruntime140,d3dcompiler_47=n,b\"` | +| Worms World Party | 2401592779 | Doesn't start | `WINEDLLOVERRIDES=\"msvcp140,vcruntime140=n,b\"` | +| Duck Game | 2926094216 | Won't launch | `WINEDLLOVERRIDES=\"msvcp140,vcruntime140=n,b\"` | + +- **Doom 3 BFG** (flatpak, appid `-2113091226`) — работает ✅ +- **Atomic Heart VK / VK GameCenter** — VK GameCenter запускается через Steam (GE-Proton10-34). Atomic Heart (97GB) установлен через GameCenter на Data. Exe GameCenter скопирован на Data (не на Windows/ — read-only). Symlink `d:` в dosdevices ведёт на `/run/media/bazzite/Data`. + +## Target: + +## Still broken — fundamental issues + +| Game | AppID | Symptom | What's known | What might help | +|------|-------|---------|--------------|-----------------| +| Atomic Bomberman / BM95 | 2875360586 | Crash "instruction at 0x0 referenced 0x7" | Win3.1-era PE32. Tried: `proton_8`, registry `Version=win98`, virtual desktop 640×480, WineBus controllers off | Dedicated 32-bit `WINEARCH=win32`; or DOSBox‑X | +| Discworld Noir | 2361018632 | "Please insert disk III" | Russian 2-CD "Zatmenie v2". 69 missing files copied ✅. Drive D: → cd2 as CD-ROM | Try cd1; or mount `.nrg` images | +| Final Fantasy X HD | 2642741094 | Was Japanese, no saves | `GameSetting.ini` replaced (en+HD) from Windows | Test pending | +| Metal Slug X | 2938712902 | Speed wrong, controls dead | Neo-Geo emulator config issue, not Wine | — | +| Metro Exodus | 3594910936 | "Unable to create interface isteamuser" | VC++ runtime copied | Needs Goldberg `steam_api64.dll` + `steam_appid.txt` | +| Split Second | 3871231787 | Green artifacts | `PROTON_USE_WINED3D=1` | Switch to GE-Proton; copy `d3dx9_*.dll` | +| Super Bomberman R2 | 3340354231 | Gamepad not detected | Steam Input config issue | — | + +## Gamepad Issues +- **Samurai Gunn, Super Bomberman R2**: not detected → Steam Input "Force Off" / "Use Default" cycle; or `SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD=1` +- **Metal Slug X**: wrong speed in Neo-Geo wrapper + +--- + +## Common Fix Recipes + +### "Doesn't start" → try vcrun2019 +``` +protontricks vcrun2019 d3dcompiler_47 +``` + +### XNA 4.0 games (Duck Game, etc.) +``` +protontricks xna40 +``` + +### DOS-era games (e.g. Atomic Bomberman) +Proton won't work. Use **Steam Tinker Launch** → wrap with DOSBox (dosbox-staging). + +### Non-Steam copy of a Steam game (e.g. Metro Exodus) +Game needs `steam_api64.dll` emulator: +1. Drop in Goldberg `steam_api64.dll` +2. Add `steam_appid.txt` with the real App ID (e.g. 412020 for Metro Exodus Enhanced) + +### Cyrillic encoding issues (e.g. Discworld Noir) +Try in order: +1. `LANG=ru_RU.UTF-8 %command%` +2. `WINEDLLOVERRIDES=\"gdiplus=n,b\"` +3. Install `ttf-mscorefonts` + set Cyrillic codepage in Wine registry + +### White screen / broken D3D9 (Rayman Origins, Split Second) +``` +PROTON_USE_WINED3D=1 %command% +``` +Or try `WINEDLLOVERRIDES=\"d3d9=b\"` or an older Proton version. + +### Doom 3 BFG Edition — чёрные текстуры, краш после интро +Proton ломает текстуры — известный регресс. **Фикс:** Luxtorpeda → rbdoom-3-bfg (native Linux engine). + +--- + +## VK Play / VK GameCenter на Bazzite + +### Аккаунты VK Play +- **Основной:** `xxxellam@gmail.com` — Google, залогинен на Bazzite +- **Дополнительный:** `mallexxx@mail.ru` — подключён к VK Play + +### Общая схема +VK Play версии игр запускаются через лаунчер VK GameCenter (`GameCenter.exe`) поверх Proton. Лаунчер отвечает за авторизацию и лицензирование (в т.ч. Denuvo-эмуляцию). + +Известно: +- VK Play официально подтвердил совместимость Atomic Heart со Steam Deck/Proton. +- Для авторизации используется `mycomgames://` URL-схема. На HTPC без Steam Deck API может потребоваться ручной ввод логина/пароля. +- Альтернатива: Lutris с конфигом из gist.github.com/keyCat (обработчик `mycomgames://` схемы). +- Перед первым запуском может понадобиться `protontricks -555655402 vcrun2019`. + +### Как добавить VK Play игру +1. Запустить VK GameCenter через Steam (GE-Proton10-34) +2. Авторизоваться в VK Play +3. Установить игру через лаунчер (Atomic Heart уже установлен) +4. После авторизации игра появится в библиотеке VK Play + +### Lutris login workaround (shared prefix with Steam/Proton) + +**Проблема:** Steam CEF browser не умеет обрабатывать `mycomgames://` URL-схему, необходимую для OAuth-логина VK Play. При клике "Login" — `ERR_NETWORK_IO_SUSPENDED`. + +**Решение:** использовать Lutris для первичной авторизации, потом запускать игру через Steam/Proton. + +### Implementation status (2026-05-28) + +Все компоненты настроены через SSH (Kraken → HTPC). Последний шаг (логин) требует GUI-сеанса на HTPC. + +| Component | Status | Notes | +|-----------|--------|-------| +| `d:` dosdevices symlink (→ Data) | ✅ | `/run/media/bazzite/Data` | +| `handle-vkplay-scheme` script | ✅ | `~/.local/bin/` — обрабатывает `mycomgames://` через Proton 10.0 | +| `.desktop` → MIME `x-scheme-handler/mycomgames` | ✅ | Зарегистрирован на `Vkplay-scheme-handler.desktop` | +| Lutris flatpak | ✅ | `flathub net.lutris.Lutris` | +| Lutris game config (shared prefix) | ✅ | `~/.config/lutris/games/vk-play-shared.yml` | +| `start-vk.cmd` in Proton prefix | ✅ | `drive_c/VKPlay/gamecenter/start-vk.cmd` | +| Steam shortcut VK GameCenter | ✅ | AppID=-1973339958, GE-Proton10-34 | +| Steam shortcut Atomic Heart VK | ✅ | AppID=-1973339958 (через GameCenter) | +| Atomic Heart installed (97GB) | ✅ | `/run/media/bazzite/Data/gamecenter/atomic heart/` | + +**Осталось сделать (на HTPC в десктоп-режиме):** +1. Открыть Lutris → VK Play Game Center → Login +2. Авторизоваться в браузере, разрешить `mycomgames://` +3. Вернуться в игровой режим +4. Запустить Atomic Heart через Steam → ожидать что токен в shared prefix работает + +1. Установить Lutris через flatpak: + ``` + flatpak install flathub net.lutris.Lutris + ``` + +2. Создать кастомный пресет VK GameCenter в Lutris, указав **тот же Wine prefix**, что использует Steam: + ``` + ~/.local/share/Steam/steamapps/compatdata/-1973339958/pfx/ + ``` + +3. Зарегистрировать `mycomgames://` protocol handler — поместить .desktop-файл, который ассоциирует схему с Lutris: + - Референс: https://gist.github.com/keyCat/a77b1d4d1e0f651b9d3a9818e7f739c5 + +4. Выполнить логин через Lutris — откроется внешний браузер, авторизация сработает, редирект `mycomgames://` попадёт в Lutris. + +5. После успешного логина запускать игру через Steam — токен уже лежит в shared prefix, `mycomgames://` больше не нужен. + +**Почему shared prefix:** игра (Atomic Heart, 97GB) НЕ копируется — она уже установлена в `/run/media/bazzite/Data/gamecenter/atomic heart/` через Steam-версию VK GameCenter. Lutris будет использовать тот же префикс, где уже есть dosdevices symlink `d:` → `/run/media/bazzite/Data` и все токены авторизации. + +--- + +## How to Add a Non-Steam Game via VDF + +**Шаг 0: Остановить Steam** +``` +systemctl --user stop gamescope-session-plus@steam.service +``` + +**Шаг 1: Прочитать shortcuts.vdf backup** +AppID в shortcuts.vdf — signed int32. В config.vdf CompatToolMapping — unsigned uint32. +```python +import vdf +# read backup +with open('shortcuts.vdf.bak-eagle', 'rb') as f: + d = vdf.binary_loads(f.read()) +# edit d['shortcuts'] +# write +with open('shortcuts.vdf', 'wb') as f: + f.write(vdf.binary_dumps(d)) +``` + +**Шаг 2: Добавить CompatToolMapping в config.vdf** +Unsigned appid = signed & 0xFFFFFFFF. + +**Шаг 3: Запустить Steam, проверить** + +**Критически важно:** +- Редактировать ТОЛЬКО при остановленном Steam +- Делать бэкап **до** изменений +- `vdf.binary_loads` — регистрозависим: `AppName`, `Exe`, а не `appname`, `exe` + +--- + +## VDF Troubleshooting + +### shortcuts.vdf испорчен (все записи пустые) +Steam перезаписывает VDF при старте если не понимает ручные правки. Восстанавливай из бэкапа. + +**Бэкапы** (в `~/.steam/steam/userdata/147839491/config/`): +| Файл | Дата | Entries | Примечание | +|------|------|---------|------------| +| `shortcuts.vdf.bak-eagle` | 23 мая 18:32 | 106 | **Последний чистый бэкап** — не тронут | +| `shortcuts.vdf.bak-launch` | 22 мая 22:25 | 108 | Старый, без Atomic Heart | +| `shortcuts.vdf.whale-restored-YYYY-MM-DD` | по ситуации | 108 | Текущее состояние после восстановления | +| `/run/media/bazzite/Data/backups/saves/shortcuts/shortcuts.vdf.YYYY-MM-DD` | daily | — | Автоматический через saves-backup.service | + +Проверка бэкапа: +```python +import vdf +with open("shortcuts.vdf.bak-eagle", "rb") as f: + d = vdf.binary_loads(f.read()) +named = sum(1 for e in d["shortcuts"].values() if e.get("AppName")) +print(f"{named} named entries") +``` + +### VDF Incident — 2026-05-27 (разбор) +**Что произошло:** при попытке добавить VK GameCenter + Atomic Heart VK через Python vdf на работающем Steam, Steam переписал файл при старте, создав 110 пустых записей-заглушек. + +**Почему бэкапы тоже пустые:** все бэкапы были сделаны уже после того как Steam испортил файл. Единственный выживший — `bak-eagle` (23 мая, до начала экспериментов). + +**Как нашли:** hexdump показал имена игр, но `vdf.binary_loads` не видел их — проблема в регистре ключей (`AppName`, а не `appname`). + +**Как восстановили:** +1. Остановлен Steam +2. Считан `bak-eagle` (106 записей) + добавлены VK GameCenter (index 106) и Atomic Heart VK (index 107) +3. Обнаружено что config.vdf уже имеет CompatToolMapping с другими appid от предыдущих попыток +4. Перегенерация с appid, совпадающими с config.vdf +5. Удалены дублирующиеся unsigned записи из config.vdf +6. Создан `shortcuts.vdf.whale-restored-2026-05-27` + +**Уроки:** +- Только при остановленном Steam +- Бэкап ДО изменений +- Проверять signed↔unsigned соответствие +- Регистр ключей в vdf.binary_loads + +--- + +## Key Infra Notes + +- **shortcuts.vdf**: `~/.steam/steam/userdata/147839491/config/shortcuts.vdf` (binary VDF) +- **config.vdf**: `~/.steam/steam/config/config.vdf` (text VDF, CompatToolMapping block) +- **Wine prefix**: `~/.local/share/Steam/steamapps/compatdata//pfx/` +- **protontricks**: Flatpak, needs `flatpak override --user --filesystem=/var/home/bazzite/.local/share/Steam` +- **Background jobs**: `systemd-run --user --unit=` +- **GE-Proton**: extract tar.gz into `~/.steam/steam/compatibilitytools.d/`, restart Steam + +### saves-backup.service +Systemd user service (`/etc/systemd/user/saves-backup.service`): +- Ежедневный rsync Steam userdata +- Копия shortcuts.vdf в `/run/media/bazzite/Data/backups/saves/shortcuts/shortcuts.vdf.YYYY-MM-DD` +- Ротация: удаление старше 30 дней + +--- + +## Fixes Done (filesystem / config / cover-art) +- Cover art swapped: Atomic Bomberman, Discworld Noir, SBR2, FFVI, ALttP, Sunset Riders +- "Captain Commando" → "Sunset Riders" +- CompatToolMapping for all AppIDs: + - **GE-Proton10-34**: Brawlhalla, Castle Crashers, Worms WMD/WP, Duck Game, Rayman Origins, VK GameCenter, Atomic Heart VK, insaneramzes Atomic Heart + - **Proton 8.0**: Atomic Bomberman, Discworld Noir, Metal Slug X, Soldat, Red Alert 3, Split Second + - **Luxtorpeda**: Doom 3 BFG + - **Proton Experimental**: ~24 remaining games +- Doom 3 BFG → Luxtorpeda (rbdoom-3-bfg, 2026-05-27) +- Rayman Legends: Ubisoft Connect + registry fix +- Discworld Noir: symlink, Cyrillic locale, missing files, drive D: as CD-ROM +- FFX HD: English `GameSetting.ini` +- VC++ runtime + d3dcompiler DLLs copied into 6 prefixes from Windows partition +- Duck Game: XNA 4.0 framework dir copied +- Atomic Bomberman: `Version=win98` + virtual desktop 640×480 + WineBus controllers off +- GE-Proton10-34 installed + +## Source Assets (Windows partition) +- Ubisoft Game Launcher: `/run/media/bazzite/Windows/Program Files (x86)/Ubisoft/` +- System DLLs: `/run/media/bazzite/Windows/Windows/System32/`, `SysWOW64/` +- XNA 4.0: `/run/media/bazzite/Windows/Program Files (x86)/Microsoft XNA/` +- .NET 4.0: `/run/media/bazzite/Windows/Microsoft.NET/Framework64/v4.0.30319/` +- Discworld Noir source: `/run/media/bazzite/Data/downloads/zatmenie v2 [torrents.ru]/` +- **Discworld Noir (скачан 2026-05-29):** `/run/media/bazzite/Data/downloads/Discworld Noir.isz` (ISZ, 1.1GB, Alcohol 120%) +- **Discworld (оригинал, скопирован 2026-05-29):** `/run/media/bazzite/Data/downloads/DISCWORLD.iso` (ISO, 434MB) — с TrueNAS `/mnt/RED_2TB/backup/Downloads/Distr/games/DISCWORLD.iso` +- FFX settings: `/run/media/bazzite/Windows/Users/mallexxx/documents/square enix/...` + +## Open Questions +1. Delete `pfx/` folders for still-broken GE-Proton games (Brawlhalla, Castle Crashers, Worms WMD/WP, Duck Game) +2. Atomic Bomberman: 32-bit Wineprefix outside Proton +3. Discworld Noir: try cd1 or mount `.nrg` images +4. Metro Exodus: Goldberg Steam emulator? +5. **Atomic Heart VK:** test VK Play авторизацию, при необходимости `protontricks vcrun2019` +6. **Doom 3 BFG:** test с Luxtorpeda → rbdoom-3-bfg + +## See Also +[[personal-os-architecture]] [[obsidian-mcp-wrapper]] [[family/how-to/htpc-steam-nonsteam-shortcuts]] diff --git a/family/how-to/htpc-kodi-layout.md b/family/how-to/htpc-kodi-layout.md new file mode 100755 index 00000000..36afc967 --- /dev/null +++ b/family/how-to/htpc-kodi-layout.md @@ -0,0 +1,31 @@ +--- +title: HTPC Kodi Layout & WoL +created: '2026-05-23' +updated: '2026-05-23' +type: tech +namespace: family +tags: [htpc, kodi, wol, infra, how-to] +related: + - "[[tech/htpc-steam-emulators]]" +--- + +# HTPC Kodi Layout & WoL + +## Wake on LAN + +HTPC MAC address stored in router DHCP config. WoL command: + +```bash +wakeonlan +``` + +Or via Kodi remote / Home Assistant automation. + +## Kodi Layout + +- Home screen: custom skin (record actual skin name here) +- Sources: NFS mounts from TrueNAS (`/mnt/RED_2TB/...`) +- Add-ons: check Kodi installed add-ons list + +Note: specific layout details were not migrated. Update this page +when next doing HTPC maintenance. diff --git a/family/how-to/htpc-magic4pc.md b/family/how-to/htpc-magic4pc.md index 5e89bb4c..db2a8397 100644 --- a/family/how-to/htpc-magic4pc.md +++ b/family/how-to/htpc-magic4pc.md @@ -92,6 +92,78 @@ TV шлёт координаты в пространстве 0–1920 × 0–108 - **Бинарь слетает** после `rpm-ostree upgrade` → пересобрать и скопировать. - **Display switching** — при смене между `:0` и `:1` xdotool перезапускается. Если переключение происходит часто (мигающие окна) — возможны краткие потери мыши. +## WebOS TV App — сборка и деплой + +TV-часть — React/Enact WebOS приложение + Node.js сервис, упакованные в IPK. Исходники: `~/Developer/magic4pc/webos/`. + +### Сборка + +```bash +cd ~/Developer/magic4pc/webos +NODE_OPTIONS=--openssl-legacy-provider npm run build +``` + +**Питфолл:** `--openssl-legacy-provider` обязателен — старый webpack несовместим с Node.js 25+. + +### Упаковка IPK + +```bash +/Users/admin/webOS_TV_SDK/CLI/bin/ares-package dist/ service/ --outdir . +``` + +**Питфолл:** нужны оба аргумента `dist/` и `service/` — без `service/` Node.js сервис не попадает в IPK. +**Питфолл:** `npm run package` = `ares-package -n` (unsigned) — TV отклоняет неподписанные пакеты. Использовать SDK-команду напрямую. + +### Деплой (deploy.sh) + +```bash +cd ~/Developer/magic4pc/webos && ./deploy.sh +``` + +Скрипт: build → package → scp → close → remove → install → launch. +Для install (subscribe mode) — прямой `ssh+script` с polling на `"state":"installed"`. + +**Питфолл:** `ares-install` / `ares-launch` ненадёжны — не ждут завершения. Использовать `luna-send dev/install` через script-враппер. + +Версия в UI отображается как `1.1.0 (YYYY-MM-DD HH:MM)` — инжектируется webpack через `process.env.BUILD_DATE`. + +## Auto-launch при включении / пробуждении TV + +Magic4pc запускает настроенное приложение при включении или пробуждении TV. + +| Файл | Расположение | Назначение | +|------|-------------|-----------| +| `magic4pc-settings` | PERSISTENT_DIR | ID выбранного приложения | +| `magic4pc-last-app` | PERSISTENT_DIR | Последнее активное приложение | +| `magic4pc-run-state` | `/tmp/` | `running` после первого запуска | + +`PERSISTENT_DIR = /media/developer/apps/usr/palm/services/me.wouterdek.magic4pc.service` + +Логика: +- **Boot/wake:** `/tmp` очищается → нет `run-state` → `freshStart=true` → запускает настроенное приложение +- **Ручной запуск:** `run-state=running` уже есть → auto-launch пропускается + +`init.d` скрипт на TV (`/var/lib/webosbrew/init.d/magic4pc`) удаляет `run-state` при suspend — wake снова тригерит fresh launch. Редактировать только в репо (`tv-scripts/init.d-magic4pc.sh`), не напрямую на TV. + +## WebOS Back Key + системная клавиатура + +Когда системная клавиатура открыта, **первый Back** поглощается OS (закрывает клавиатуру) — `keydown` и `onButtonDown` **не стреляют**. Второй Back приходит нормально. + +Фикс в `MainPanel.js` (флаг `_kbWasOpen`): + +```js +// Input.onActivate: +this._kbWasOpen = true; + +// onButtonDown на Back: +if (this.state.wolMacActive || this._kbWasOpen) { + this._kbWasOpen = false; + return; // подавить закрытие панели +} +``` + +Подходы, которые не работают: `Popup.noAutoDismiss`, timeout-эвристика, polling `document.activeElement` (клавиатура — системный оверлей, фокус не переходит на INPUT). + ## Деплой и sudoers На HTPC настроен sudo без пароля для systemctl: diff --git a/family/how-to/htpc-system.md b/family/how-to/htpc-system.md index cfc962d4..fa75ed33 100644 --- a/family/how-to/htpc-system.md +++ b/family/how-to/htpc-system.md @@ -229,6 +229,20 @@ Imported 185 torrents from Windows Transmission-daemon into HTPC Transmission. 10 torrents had no data (X:\Movies\ — lost disk, permanently gone). +## Sudo / Пароль + +Пароль пользователя `bazzite`: **`bazzite`** + +Для удалённых команд через SSH: +```bash +ssh bazzite@192.168.1.86 'echo bazzite | sudo -S mount ...' +``` + +Также настроен sudo без пароля для systemctl (см. [[htpc-magic4pc#Деплой и sudoers]]): +``` +bazzite ALL=(ALL) NOPASSWD: /usr/bin/systemctl +``` + ## OSTree / Обновления - `/usr/local/bin` слетает при `rpm-ostree` upgrade (новый `/usr` overlay) - Бинари держать в `/home/bazzite/` или пересобирать после обновлений diff --git a/family/how-to/jellyfin-config.md b/family/how-to/jellyfin-config.md new file mode 100755 index 00000000..e8d03731 --- /dev/null +++ b/family/how-to/jellyfin-config.md @@ -0,0 +1,33 @@ +--- +title: Jellyfin Config +created: '2026-05-23' +updated: '2026-05-23' +type: tech +namespace: family +tags: [jellyfin, infra, media, credentials] +related: + - "[[tech/arr-stack-kraken]]" +--- + +# Jellyfin Config + +**URL:** `http://kraken:8096` + +## Users + +| User | Password | Notes | +|------|----------|-------| +| alex | (set) | Admin | +| lisa | (none) | Cartoons library only | + +## API Key + +`87af49b6ff62ea68da6abdab0d7a4fbc` + +Used by automation scripts (watchlist-sync, media-pipeline agents). + +## Database / Maintenance + +- **Before any DB operation:** `docker stop jellyfin` first +- Jellyfin runs as a Docker container on Kraken +- Config stored in `/opt/jellyfin/config/` (check docker-compose for exact path) diff --git a/family/how-to/jellyfin-transcode-rpi5.md b/family/how-to/jellyfin-transcode-rpi5.md new file mode 100755 index 00000000..5d3fe57c --- /dev/null +++ b/family/how-to/jellyfin-transcode-rpi5.md @@ -0,0 +1,108 @@ +--- +title: Jellyfin Транскод на RPi5 — PGS/Subtitle Pitfalls +type: reference +namespace: work +tags: + - jellyfin + - kraken + - transcode + - subtitles + - pitfalls +created: '2026-05-18' +updated: '2026-05-18' +--- +# Jellyfin Транскод на RPi5 — PGS/Subtitle Pitfalls + +## Контекст + +Клиент: Konka TV с WebOS. Сервер: Raspberry Pi 5 (Крaken, ARM64). + +## Direct Play условия + +Direct Play работает при выполнении всех условий: +- Аудио: AAC +- Субтитры: **отсутствуют** (или внешние SRT, без burn-in) + +## Проблема PGS/ASS субтитров + +PGS и ASS (SSA) субтитры вызывают **burn-in** — Jellyfin рендерит субтитры прямо в видеопоток. + +**Результат:** полный транскод на CPU → **275% CPU load** на RPi5 → лаги, перегрев. + +**Экспериментальный PGS rendering** в настройках Jellyfin — **не помогает** на RPi5. + +## Решение: конвертация PGS → SRT + +Установка через uv (arm64/aarch64 совместимо): +```bash +~/.local/bin/uv tool install pgsrip +sudo apt-get install -y libgl1 libglib2.0-0 tesseract-ocr tesseract-ocr-rus tesseract-ocr-eng +``` + +Скрипт конвертации: +``` +/srv/dev-disk-by-uuid.../docker/media-pipeline/pgs-to-srt.sh +``` + +```bash +# один файл +pgs-to-srt.sh /media/movies/Film.mkv + +# вся директория +pgs-to-srt.sh /media +``` + +Логи: `/srv/.../docker/media-pipeline/pgs-to-srt.log` + +Скрипт: +- Скипает файлы без PGS-дорожек (S_HDMV) +- Скипает если .srt уже есть рядом +- Пишет лог с timestamp + +После конвертации Jellyfin читает внешние `.srt` без транскода. + +## Transmission completion hook + +Хук `/config/on-download-complete.sh` (монтируется в контейнер `:ro`) после завершения загрузки: +1. Запускает media-pipeline sync +2. Запускает pgs-to-srt.sh для скачанного файла/директории + +Лог: `/srv/.../docker/media-pipeline/download-complete.log` + +## Итог по субтитрам + +| Тип | Direct Play | Транскод | +|-----|------------|---------| +| Нет субтитров | ✅ | — | +| Внешний SRT | ✅ | — | +| ASS/SSA | ❌ | burn-in, 275% CPU | +| PGS (MKS/MKV) | ❌ | burn-in, 275% CPU | + +## NFD/NFC filename encoding pitfall + +**Проблема:** HTPC сохраняет имена файлов в NFD, Kraken — в NFC. +→ Jellyfin видит **две** папки: одна с `.avi` файлом (NFD), другая с `.nfo` (NFC). +→ NFO не применяется — Jellyfin берёт NFD-папку (с видео) и игнорирует NFC-папку (с NFO). +→ Метаданные пустые. + +**Фикс A (быстрый):** скопировать `movie.nfo` + `poster.jpg` в NFD-папку: +```bash +# python3 — использовать bytes для путей +import os, shutil +nfc_path = "…/Movie.NFC/movie.nfo" +nfd_path = "…/Movie.NFD/movie.nfo" +shutil.copy(nfc_path.encode(), nfd_path.encode()) +``` + +**Фикс B (радикальный):** переименовать всё в NFC (нормализовать). + +**Диагностика:** +```bash +ls -la | cat -v # покажет NFD-escape символы как ^ escape sequences +python3 -c "import os; [print(repr(f)) for f in os.listdir('.')]" +``` + +## Связанные страницы + +- [[concepts/kraken-media-stack]] — полный медиастек: *arr + Jellyfin + media-pipeline +- [[tech/kraken-network]] — инфра Кракена, HDD error recovery diff --git a/family/how-to/kraken-network.md b/family/how-to/kraken-network.md new file mode 100755 index 00000000..fa702dc2 --- /dev/null +++ b/family/how-to/kraken-network.md @@ -0,0 +1,34 @@ +--- +title: Kraken Network & Infra +created: '2026-05-23' +updated: '2026-05-23' +type: tech +namespace: personal +tags: [infra, kraken, ssh, wireguard, network] +related: + - "[[tech/arr-stack-kraken]]" +--- + +# Kraken Network & Infra + +## SSH Access + +``` +ssh kraken +``` + +IP: `192.168.1.15` (wlan0, primary). SSH alias `kraken` resolves via `~/.ssh/config`. + +## WireGuard Topology + +Split-tunnel: Eagle ↔ VPS ↔ Kraken. Full details: [[tech/wireguard-vpn]]. + +- Eagle: `10.99.0.2`, Kraken: `10.99.1.2`, VPS relay: `10.99.0.1`/`10.99.1.1` +- `wg-auto.sh` on Eagle (LaunchDaemon) — up when off home Wi-Fi, down at home +- VPS as relay; two interfaces (wg0/wg1) avoid hairpin forwarding + +## Media Volume Mount Paths + +Docker containers on Kraken mount media from NAS over NFS/SMB. +Paths were documented here — check docker-compose files in +`/opt/media-toolbox-kraken` for current mount config. diff --git a/family/how-to/truenas-inpxer.md b/family/how-to/truenas-inpxer.md new file mode 100755 index 00000000..5752f6d8 --- /dev/null +++ b/family/how-to/truenas-inpxer.md @@ -0,0 +1,30 @@ +--- +title: TrueNAS Inpxer / Books Setup +created: '2026-05-23' +updated: '2026-05-23' +type: tech +namespace: family +tags: [truenas, books, infra, storage] +related: + - "[[tech/kraken-network]]" +--- + +# TrueNAS Inpxer / Books Setup + +## Books Storage Path + +``` +/mnt/RED_2TB/storage/books/ +``` + +Books are stored on the RED_2TB pool. Inpxer (or similar indexer) serves +the library from this location. + +## Related TrueNAS Notes + +- General TrueNAS access: [[family/how-to/truenas-access]] +- Remote access reverse proxy: [[personal/docs/truenas-remote-access-reverse-proxy]] +- Rclone backup: [[family/how-to/truenas-rclone-backup]] + +Note: Inpxer-specific config was not migrated — add details here when +revisiting this setup. diff --git a/family/how-to/tv-luna-send.md b/family/how-to/tv-luna-send.md index ee90a1dc..c4a10c05 100644 --- a/family/how-to/tv-luna-send.md +++ b/family/how-to/tv-luna-send.md @@ -40,6 +40,20 @@ Переменная `TV_HOST` переопределяет таргет (default: `root@LGwebOSTV`, alias `root@tv`, IP: `root@192.168.1.75`). DNS резолвится через роутер (192.168.1.1), см. [[router-bishkek-asus]]. +## Common Luna URIs + +| URI | Назначение | +|-----|-----------| +| `.../applicationmanager/running` | Список запущенных приложений | +| `.../applicationmanager/closeByAppId` | Закрыть приложение по ID | +| `.../appInstallService/dev/install` | Установить IPK (subscribe) | +| `.../appInstallService/dev/remove` | Удалить приложение | +| `.../applicationManager/launch` | Запустить приложение | +| `.../applicationManager/getForegroundAppInfo` | Текущее активное приложение | +| `...magic4pc.service/query` | Статус сервиса magic4pc | + +Полные префиксы: `com.webos.service.applicationmanager`, `com.webos.appInstallService`, `com.webos.applicationManager`. + ## Ограничение `luna-send -i` (subscribe, бесконечный) не работает через враппер. Для install (`dev/install`) используется прямой ssh+script с polling в deploy.sh. diff --git a/family/how-to/vault-git-sync.md b/family/how-to/vault-git-sync.md new file mode 100755 index 00000000..8c93bb2f --- /dev/null +++ b/family/how-to/vault-git-sync.md @@ -0,0 +1,94 @@ +--- +title: Vault Git Sync — Setup & Pitfalls +created: '2026-05-23' +updated: '2026-05-23' +type: tech +namespace: wiki +tags: [vault, git, sync, infra, obsidian] +--- + +# Vault Git Sync + +Obsidian vault is a bare git repo on TrueNAS (`mallexxx.duckdns.org:/mnt/RED_2TB/storage/git/obsidian-vault.git`). +Three hosts sync to it: Eagle, Kraken, Taiga. + +## Sync Strategy (correct) + +All hosts must follow this order: + +``` +1. stash local changes (git stash) +2. pull from remote (git fetch + git merge) +3. pop stash (git stash pop) +4. commit if anything new (git add -A + git commit) +5. push (git push) +``` + +**Why this order matters:** `git add -A` before pull stages deletions of files +that exist on remote but not locally. This caused Taiga to delete 32+ wiki files +on 2026-05-23 (two incidents: 3a44a55, c9020c4). + +## Scripts + +| Host | Script | Status | +|--------|------------------------------------|----------------| +| Eagle | `~/scripts/sync-vault.sh` | ✅ Correct | +| Kraken | `~/scripts/sync-vault-partial.sh` | ✅ Correct | +| Taiga | `/opt/data/sync-vault.sh` | ✅ Fixed 2026-05-23 | + +All scripts now use scoped `git add personal/ family/ .obsidian/` (not `git add -A`) before stash. +Taiga additionally uses sparse checkout (only `personal/` and `family/`) — double safeguard: +even if a bug reintroduces `git add -A`, sparse checkout means wiki/ is never checked out locally. + +## Taiga Architecture + +Taiga is TrueNAS running Hermes in Docker (`/mnt/RED_2TB/docker/hermes/`). +The sync script runs **inside the container** with: +- `GIT_WORK_TREE=/vault` → mounted from `/mnt/RED_2TB/storage/obsidian` +- `GIT_DIR=/vault.git` → mounted from `/mnt/RED_2TB/storage/git/obsidian-vault.git` (the bare repo itself) + +Taiga IS the repo — no remote push needed. Eagle pushes to `nas/main`, which is this same bare repo. + +Script location: `/mnt/RED_2TB/docker/hermes/config/sync-vault.sh` (= `/opt/data/sync-vault.sh` inside container) + +SSH access: `ssh taiga` (alias in ~/.ssh/config → `truenas_admin@mallexxx.duckdns.org`) + +To deploy a script fix: +```bash +scp ~/scripts/sync-vault-taiga.sh taiga:/mnt/RED_2TB/docker/hermes/config/sync-vault.sh +``` + +**NEVER use local IP for TrueNAS** — always `mallexxx.duckdns.org` or `ssh taiga`. + +## .gitignore + +Plugin binaries are excluded to prevent cross-device obsidian-git version conflicts: + +``` +.obsidian/plugins/obsidian-git/main.js +.obsidian/plugins/obsidian-git/styles.css +.obsidian/plugins/obsidian-git/manifest.json +``` + +Each device manages its own plugin binaries via Obsidian's built-in update mechanism. +`data.json` (plugin config) IS tracked — shared settings across devices. + +## Incident: 2026-05-23 + +**Root cause:** Taiga's `/opt/data/sync-vault.sh` did `git add -A` before `git pull`. + +**Sequence:** +- 22:02 UTC Eagle created 15 wiki files (d757029, 6dc4b89) +- 00:00 UTC Taiga ran sync → staged deletions (local didn't have wiki/) → commit 3a44a55 +- 03:02 UTC Taiga ran again → deleted 8 more files (c9020c4) + +**Recovery:** `git checkout -- ` for each file from last-good commits. +32 files restored in commit 620e2df. + +## Stash Cleanup + +Orphaned stashes from obsidian-git mobile syncs accumulate. Safe to drop: +```bash +git stash drop stash@{N} # drop specific, or: +git stash clear # drop all (only if no unrecovered work) +``` diff --git a/family/how-to/vps-qentra.md b/family/how-to/vps-qentra.md new file mode 100755 index 00000000..b53cc32c --- /dev/null +++ b/family/how-to/vps-qentra.md @@ -0,0 +1,74 @@ +--- +title: VPS qentra.top +created: '2026-05-24' +updated: '2026-05-27' +type: tech +namespace: personal +tags: [infra, vps] +confidence: medium +related: + - "[[tech/kraken-network]]" + - "[[tech/wireguard-vpn]]" +--- + +# VPS qentra.top + +**IP:** 91.207.28.205 +**Stack:** nginx + Python 3.11, Cloudflare proxy +**Panels:** panel.qentra.top:5430, v.qentra.top:8964 + +## Subdomain Setup + +1. Add Cloudflare A-record → 91.207.28.205 +2. Add nginx vhost in `/etc/nginx/sites-enabled/` + +## Backup + +Ежедневный бэкап конфигов через cron (04:00): +- Скрипт: `/root/vps-backup.sh` +- Cron: `/etc/cron.d/vps-backup` +- Куда: `/root/backups/` (`vps-config-YYYY-MM-DD_HHMMSS.tar.gz`) +- Ретеншн: 90 дней + +**Что бэкапится:** +| Файл | Описание | +|------|----------| +| `xray/config.json` | Xray конфиг (все inbound/outbound, клиенты) | +| `x-ui/config.json` | 3X-UI панель (полная копия клиентов) | +| `systemd/xray.service` | systemd unit Xray | +| `cron/xray-update` | Еженедельное обновление Xray и 3X-UI | +| `nginx/sites-enabled/nolvu` | nginx vhost → nolvu.qentra.top | +| `nginx/sites-available/panel` | nginx vhost → panel.qentra.top | +| `nginx/sites-available/xray` | nginx vhost → v.qentra.top (WS прокси) | +| `nginx/nginx.conf` | Основной конфиг nginx | +| `scripts/add-client.sh` | OpenVPN скрипты | +| `scripts/revoke-client.sh` | OpenVPN скрипты | +| `scripts/setup-openvpn.sh` | OpenVPN скрипты | + +**Восстановление:** +```bash +tar xzf /root/backups/vps-config-latest.tar.gz -C / +# После восстановления — перезапустить сервисы: +systemctl daemon-reload +systemctl restart xray nginx +``` + +## Xray (VLESS+REALITY) + +- **Inbound 443** — REALITY, 4 клиента + - `2D9F24C4-21FE-4784-9843-F11C384DA67A` — `user1` (телефон) + - `f80b579b-baa6-4887-a76e-682635335c15` — `natali` + - `62814419-722a-4f45-b560-eaed2d0df6af` — `alexander_martemyanov` + - `23aa4d8e-ef16-43ab-bdef-3d2c11d8ef02` — `taiga` (TrueNAS vless-proxy) +- **Inbound 8964** — WebSocket, через nginx v.qentra.top/qentra + - `2D9F24C4-21FE-4784-9843-F11C384DA67A` — `124hyews` + - `d10834e6-ba84-4dbd-b5e6-b5f4df9e64d6` — `natali@duck.com` +- PrivateKey: `CMu-Sz49V5HW-s2c0P33EDlCwY-JMhQbxfdn7LhvY1A` +- Публичный ключ (для клиентов): `Rtkptj9Cij2go_oE0Klgf_Mwfhq-d_oaW4mQubHzXm0` + +**Важно:** REALITY на 443, nginx на 8443 + 80. При перезапуске — сначала Xray (забирает 443), потом nginx. + +## See Also + +- [[tech/kraken-network]] — Kraken home network topology +- [[tech/wireguard-vpn]] — WireGuard tunnel used for Eagle↔VPS traffic diff --git a/family/how-to/wireguard-vpn.md b/family/how-to/wireguard-vpn.md index e54d1a42..40536ab6 100644 --- a/family/how-to/wireguard-vpn.md +++ b/family/how-to/wireguard-vpn.md @@ -94,3 +94,8 @@ ssh kraken "sudo wg show" # Логи auto-connect Eagle cat /var/log/wg-auto.log ``` + +## Связанные заметки + +- [[kraken-network]] — SSH к Кракену, пути медиаволюмов +- [[personal-os-architecture]] — Eagle hardware, домашняя инфра diff --git a/family/projects/watchlist-automation.md b/family/projects/watchlist-automation.md new file mode 100755 index 00000000..b3ac4813 --- /dev/null +++ b/family/projects/watchlist-automation.md @@ -0,0 +1,150 @@ +--- +created: '2026-05-20' +updated: '2026-05-27' +status: active +tags: + - kraken + - watchlist + - automation + - movies +--- +# Watchlist Automation — Полный Flow + +Полная цепочка: как `family/documents/movies-watchlist.md` превращается в скачанный контент. + +## Полная цепочка + +``` +[вс 09:00] watchlist-discover ← KP API топ фильмов 2024-2025 + ↓ добавляет в секцию "Новинки 👍/👎" + movies-watchlist.md + Alex ставит 👍 / 👎 + ↓ +[ежедн 09:00] watchlist-resolve ← SSH Кракен → resolve → commit +[ежедн 10:00] watchlist-sync-down + → process-thumbs (перемещает 👍 → начало списка) + → sync-down (добавляет батч в Radarr/Sonarr) + ↓ +Radarr (7878) / Sonarr (8989) + → Prowlarr (9696) → RuTracker / Kinozal(M) / NoNaMe / etc. + → Transmission (9091) + → /media/movies-radarr или /media/series-sonarr + ↓ +router.py (Custom Script) → читает жанры из NFO/TMDb + → symlink в /media/{movies,cartoons,documentaries,...}/ + ↓ +Jellyfin (8096) → rescan → виден контент +``` + +## Статусы в movies-watchlist.md + +| Символ | Значение | Действие | +|--------|---------|---------| +| `[ ]` | не смотрел, в очереди | sync-down добавляет в Radarr/Sonarr | +| `[x]` | просмотрено | sync-up из Jellyfin watched history | +| `⬇️` | добавлено в Radarr/Sonarr | ждёт скачки | +| `👍` | нравится (новинка) | process-thumbs переносит в начало → попадёт в sync-down | +| `👎` | не хочу (новинка) | process-thumbs перемещает в `## Новинки👎` | +| `🗓️` | upcoming (ещё не вышел) | discover помечает, не скачивается | +| `❓` | не найдено / ambiguous | ждёт ручного уточнения | +| `🧟‍♀️` | horror (genre_id=27) | информационный маркер | + +## Структура секций movies-watchlist.md + +``` +## Новинки - 👍/👎 ← discover добавляет сюда +## Фильмы ← основной список, отступ 2 пробела ( - [ ]) +## Мультфильмы +## Аниме +## Новинки👎 ← без пробела перед 👎 +## Связанные заметки +``` + +**Важно:** `process_thumbs` автоматически определяет отступ из `## Фильмы` и сохраняет при переносе. `SECTION_THUMBDN = "## Новинки👎"` (без пробела) — должно точно совпадать с заголовком. + +## Cron Jobs (на Кракене с 2026-05-27) + +Весь watchlist pipeline работает на Кракене (мигрировал с Eagle 2026-05-27). + +| Job | Расписание | Скрипт | +|-----|-----------|--------| +| `watchlist-nightly` | ежедн. 01:00 | `~/.hermes/scripts/watchlist-nightly.sh` | +| `watchlist-discover` | вс 09:00 | `~/.hermes/scripts/watchlist-discover.sh` | + +**watchlist-nightly.sh** (на Кракене): +1. `sync-vault.sh` (pull) → `process-thumbs --apply` → `sync-vault.sh` (push) +2. `resolve --recheck --apply` → `sync-down --apply --batch 5` → `sync-up --apply` → `sync-vault.sh` (push) + +**Пути на Кракене (HDD UUID 49e8f586-3839-4c5d-a1e1-58bfc3579ade):** +- `/home/kraken/obsidian` → symlink на `/srv/dev-disk-by-uuid-.../obsidian` +- watchlist-sync: `/srv/dev-disk-by-uuid-.../Developer/watchlist-sync` + +## Discover — Фильтрация + +| Тип | Порог | +|-----|-------| +| Мировые | IMDb ≥ 7.0 или KP ≥ 7.0 | +| Российские | KP ≥ 8.5 (защита от накрутки) | +| Upcoming | год ≥ текущий−1 | +| Минимум голосов | IMDb ≥ 1000 или KP ≥ 500 | + +KP API endpoint: `https://api.poiskkino.dev/v1.4/movie` (redirect с api.kinopoisk.dev). + +## *arr Stack — Индексеры (Prowlarr) + +| ID | Имя | Тип | Примечание | +|----|-----|-----|-----------| +| 1 | RuTracker | semi-private | блокирует при rate limit на сутки | +| 2 | RuTor | public | поиск по imdbid | +| 3 | Byrutor | public | | +| 4 | NoNaMe Club | semi-private | анонимный | +| 5 | Kinozal | semi-private | резерв | +| 6 | Kinozal (M) | semi-private | магнет ✅ — без grab limit, **предпочтительный** | + +**Pi-hole питфолл:** блокирует `skyhook.sonarr.tv` → Sonarr/Radarr поиск молча не запускается. Фикс: `dns: [8.8.8.8]` в docker-compose.yml. + +## Router Script — Маршрутизация по жанру + +После импорта Sonarr/Radarr: `router.py --apply` создаёт symlinks: +- Animation (не R/18+) → `/media/cartoons/` или `/media/cartoons-series/` +- Documentary → `/media/documentaries/` или `/media/documentaries-series/` +- Прочее → `/media/movies/` или `/media/series/` + +Копирует NFO + постер рядом с symlink (Jellyfin не видит метадату через symlink напрямую). + +## watchlist-sync — Python CLI + +Репо: `~/Developer/watchlist-sync` (GitHub: `mallexxx/watchlist-sync`). + +``` +watchlist-sync resolve — TMDB + KP API → ID, тип (movie/tv) +watchlist-sync sync-down — добавить батч (max 5) в Radarr/Sonarr, поставить ⬇️ +watchlist-sync sync-up — Jellyfin watched → [x] в .md +``` + +**Резолвер:** TMDB `/search/multi` + KP fallback. Пороги: `min_rating: 6.0`, `min_score: 0.75`. При 2+ кандидатах → `❓` (LLM разрешает в cron каждые 6ч, нерешённые → уточнение в Zulip). + +**Stage 2 (после ≥50 просмотров):** AI-curated recommendations — три списка (с женой / один / семья) на основе TMDB профиля, добавляются в конец watchlist как `## 🤖 Рекомендации (YYYY-WNN)`. + +Jellyfin library mapping: Movies → `movies`, Cartoons → `mixed` (movies + episodes). + +## Питфоллы + +### KP Markdown Link Parsing Bug +Фильмы из Kinopoisk с синтаксисом `[Title](https://kinopoisk.ru/film/12345/) (2025)` передавались как есть в TMDB search → не находились → помечались `❓` навсегда (`not i.ambiguous` пропускает при следующем запуске). Фикс: commit `2db1ed9` — извлекать plain title из markdown link до передачи в TMDB. Ошибочно помеченные `❓` требуют ручного `--recheck`. + +### KP API Endpoint +`api.kinopoisk.dev` редиректит на `api.poiskkino.dev/v1.4/movie`. Использовать destination redirect напрямую. + +## Open Tasks (2026-05-20) + +- 🟡 Передавать Transmission locations через router script при каждом импорте (сейчас вручную) +- 🔴 rsync невыполненных фильмов с HTPC на Кракен (`Фильмы не перенесенные с htpc на кракен.md`) +- 🟡 Watchlist sync-up: добавить перенесённые фильмы в нужные разделы + +## Связанные заметки + +- [[watchlist-discover]] — шаг discover: KP API, format, CLI команды +- [[watchlist-sync]] — основной проект (resolve, sync-down, sync-up) +- [[arr-stack-kraken]] — детальные питфоллы *arr стека +- [[jellyfin-transcode-rpi5]] — Jellyfin PGS/ASS питфоллы diff --git a/family/projects/watchlist-discover.md b/family/projects/watchlist-discover.md index 3e0d5702..7d3b6606 100644 --- a/family/projects/watchlist-discover.md +++ b/family/projects/watchlist-discover.md @@ -76,12 +76,12 @@ watchlist-sync process-thumbs --apply --- -## Cron jobs (Hermes) +## Cron jobs (Hermes, на Кракене с 2026-05-27) | Job | Schedule | Действие | |-----|----------|----------| -| `watchlist-discover` (`6aba6979b8c4`) | вс 09:00 | discover --apply + git commit | -| `watchlist-sync-down` (`7d12b6d48786`) | ежедн. 10:00 | process-thumbs --apply → sync-down --apply | +| `watchlist-discover` | вс 09:00 | discover --apply + git commit (Кракен) | +| `watchlist-nightly` | ежедн. 01:00 | process-thumbs → resolve → sync-down → sync-up (Кракен) | --- diff --git a/personal/projects/media-pipeline-pitfalls.md b/personal/projects/media-pipeline-pitfalls.md new file mode 100755 index 00000000..ad788003 --- /dev/null +++ b/personal/projects/media-pipeline-pitfalls.md @@ -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 diff --git a/personal/projects/personal-os/agent-memory-architecture.md b/personal/projects/personal-os/agent-memory-architecture.md new file mode 100755 index 00000000..1d483cf8 --- /dev/null +++ b/personal/projects/personal-os/agent-memory-architecture.md @@ -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]] — как система эволюционирует diff --git a/personal/projects/personal-os/autonomous-agent-safety.md b/personal/projects/personal-os/autonomous-agent-safety.md new file mode 100755 index 00000000..f601257e --- /dev/null +++ b/personal/projects/personal-os/autonomous-agent-safety.md @@ -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 diff --git a/personal/projects/personal-os/hermes-deployment-patterns.md b/personal/projects/personal-os/hermes-deployment-patterns.md new file mode 100755 index 00000000..eebbc98d --- /dev/null +++ b/personal/projects/personal-os/hermes-deployment-patterns.md @@ -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. diff --git a/personal/projects/personal-os/hermes-native-vs-docker.md b/personal/projects/personal-os/hermes-native-vs-docker.md new file mode 100755 index 00000000..148ae05f --- /dev/null +++ b/personal/projects/personal-os/hermes-native-vs-docker.md @@ -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]] — сетевая топология diff --git a/personal/projects/personal-os/knowledge-lifecycle.md b/personal/projects/personal-os/knowledge-lifecycle.md new file mode 100755 index 00000000..a64c0a1b --- /dev/null +++ b/personal/projects/personal-os/knowledge-lifecycle.md @@ -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]] — как система эволюционирует через корректировки diff --git a/personal/projects/personal-os/multi-agent-design-patterns.md b/personal/projects/personal-os/multi-agent-design-patterns.md new file mode 100755 index 00000000..91b23ecc --- /dev/null +++ b/personal/projects/personal-os/multi-agent-design-patterns.md @@ -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) diff --git a/personal/projects/personal-os/multi-host-cron-topology.md b/personal/projects/personal-os/multi-host-cron-topology.md new file mode 100755 index 00000000..031dccd6 --- /dev/null +++ b/personal/projects/personal-os/multi-host-cron-topology.md @@ -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 diff --git a/personal/projects/personal-os/obsidian-mcp-wrapper.md b/personal/projects/personal-os/obsidian-mcp-wrapper.md new file mode 100755 index 00000000..05f6f805 --- /dev/null +++ b/personal/projects/personal-os/obsidian-mcp-wrapper.md @@ -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. diff --git a/personal/projects/personal-os/personal-os-agent-rules.md b/personal/projects/personal-os/personal-os-agent-rules.md new file mode 100755 index 00000000..07062598 --- /dev/null +++ b/personal/projects/personal-os/personal-os-agent-rules.md @@ -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, + '', '', '', + ARRAY[]::text[], ''); +``` + +`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`. diff --git a/personal/projects/personal-os/personal-os-architecture.md b/personal/projects/personal-os/personal-os-architecture.md new file mode 100755 index 00000000..2325a1ce --- /dev/null +++ b/personal/projects/personal-os/personal-os-architecture.md @@ -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/ +``` diff --git a/personal/projects/personal-os/personal-os-catchup-plan-2026-04-27.md b/personal/projects/personal-os/personal-os-catchup-plan-2026-04-27.md new file mode 100755 index 00000000..4bc52e44 --- /dev/null +++ b/personal/projects/personal-os/personal-os-catchup-plan-2026-04-27.md @@ -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/-`) → 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]] diff --git a/personal/projects/personal-os/personal-os-purpose.md b/personal/projects/personal-os/personal-os-purpose.md new file mode 100755 index 00000000..93e43055 --- /dev/null +++ b/personal/projects/personal-os/personal-os-purpose.md @@ -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 diff --git a/personal/projects/personal-os/personal-os-schema.md b/personal/projects/personal-os/personal-os-schema.md new file mode 100755 index 00000000..0ab9e5bb --- /dev/null +++ b/personal/projects/personal-os/personal-os-schema.md @@ -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]] diff --git a/personal/projects/personal-os/personal-os-self-modification.md b/personal/projects/personal-os/personal-os-self-modification.md new file mode 100755 index 00000000..c299b1b3 --- /dev/null +++ b/personal/projects/personal-os/personal-os-self-modification.md @@ -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/.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/.md && git commit -m "[YYYY-MM-DD] prompt: "` + +**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 "` + +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] "` + +### 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, + '', '', '', + ARRAY[]::text[], ''); +``` + +--- + +## 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 diff --git a/personal/projects/personal-os/personal-os-state-2026-04-27.md b/personal/projects/personal-os/personal-os-state-2026-04-27.md new file mode 100755 index 00000000..b7f798a2 --- /dev/null +++ b/personal/projects/personal-os/personal-os-state-2026-04-27.md @@ -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--/`) → 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]] diff --git a/personal/projects/personal-os/personal-os-sync-pipeline.md b/personal/projects/personal-os/personal-os-sync-pipeline.md new file mode 100755 index 00000000..590ce9c9 --- /dev/null +++ b/personal/projects/personal-os/personal-os-sync-pipeline.md @@ -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]] diff --git a/personal/projects/personal-os/research-queue.md b/personal/projects/personal-os/research-queue.md new file mode 100755 index 00000000..be25939f --- /dev/null +++ b/personal/projects/personal-os/research-queue.md @@ -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) diff --git a/personal/projects/personal-os/vault-agent-integration.md b/personal/projects/personal-os/vault-agent-integration.md new file mode 100755 index 00000000..32819d4d --- /dev/null +++ b/personal/projects/personal-os/vault-agent-integration.md @@ -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]] diff --git a/personal/projects/personal-os/vault-filling-guide.md b/personal/projects/personal-os/vault-filling-guide.md new file mode 100755 index 00000000..8cdba0cf --- /dev/null +++ b/personal/projects/personal-os/vault-filling-guide.md @@ -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 diff --git a/personal/projects/personal-os/vault-namespace.md b/personal/projects/personal-os/vault-namespace.md new file mode 100755 index 00000000..c9a71fd8 --- /dev/null +++ b/personal/projects/personal-os/vault-namespace.md @@ -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. diff --git a/personal/projects/personal-os/wiki-ingest-process.md b/personal/projects/personal-os/wiki-ingest-process.md new file mode 100755 index 00000000..5e801b25 --- /dev/null +++ b/personal/projects/personal-os/wiki-ingest-process.md @@ -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/`. +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/ +content_hash: +namespace: work +last_synced: +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 — + +Processed: N files +Created: N new pages +Updated: N pages +Skipped: N (unchanged) + +Pages updated: +- wiki/ (source: raw/) +``` + +## 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]] diff --git a/personal/projects/personal-os/wiki-schema.md b/personal/projects/personal-os/wiki-schema.md new file mode 100755 index 00000000..e5788d9b --- /dev/null +++ b/personal/projects/personal-os/wiki-schema.md @@ -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/ +content_hash: +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 +``` diff --git a/personal/projects/reflect-skip-fuse.md b/personal/projects/reflect-skip-fuse.md new file mode 100755 index 00000000..4323473c --- /dev/null +++ b/personal/projects/reflect-skip-fuse.md @@ -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 diff --git a/personal/user-profile.md b/personal/user-profile.md new file mode 100755 index 00000000..b956791a --- /dev/null +++ b/personal/user-profile.md @@ -0,0 +1,152 @@ +--- +namespace: personal +created: '2026-05-15' +updated: '2026-05-16' +last_synced: '2026-05-16' +tags: + - profile + - user + - alex +confidence: 0.9 +--- +# User Profile — Alex + +> Этот файл читают enrichment- и monitoring-агенты как источник истины о пользователе. +> Обновлять при любых изменениях в жизни. Дата последнего обновления: 2026-05-15. + +--- + +## Идентификация + +- **Имя:** Александр (Alex) +- **ДР:** 11.08.1987, Лев +- **GitHub:** mallexxx +- **ADHD** — диагностирован. Гиперфокус, трудности с переключением, поздний старт (~11:00), уходит в работу до 20:00+ + +--- + +## Локации + +| Место | Статус | +|-------|--------| +| Бишкек | Основное проживание. Коворкинг для работы. WireGuard домой → Kraken. | +| Новосибирск (Тайга) | Дом. Летний отпуск + зимовка (ноябрь–март). | +| Алтай | Дача, строится. | +| Дубай | Жил 2 года. Разрабатывал Quran Memorization Program для iPad. | + +--- + +## Семья + +- **Жена:** Наталия +- **Дочь:** Лиза, 7 лет — [[father-profile]] для деталей +- Живут вместе в Бишкеке + +--- + +## Работа + +- **Должность:** macOS Browser Developer / DRI +- **Компания:** DuckDuckGo (async-first, Asana + Zulip) +- Приносит доход, но устал. Хочет в инди. +- **Направление:** serial indie startuper — найти большое решение, реализовать одну небольшую фичу хорошо. Пока сложно с идеями. +- **Активный проект:** AI-психолог / рефлексия-компаньон (Reflect) — приложение для психологической рефлексии без sycophancy. Anti-sycophancy via dual-bot архитектура (Narrator + Analyst). Стек: Skip.tools (SwiftUI → Android), Claude Sonnet/Haiku backend, OpenAI Realtime API для голоса. Планируется в App Store. Юрисдикция — Казахстан ИП или Эстония OÜ. +- **Идеи приложений:** [[app-ideas]] + +--- + +## Навыки + +### Разработка +- Swift / macOS / iOS — основной стек (DDG browser, AXPressDeck, Quran app) +- JavaScript / Node.js +- Docker, WireGuard, Home Assistant, Zigbee +- Arduino, прошивки микроконтроллеров +- AI agents, MCP, LLM orchestration (personal-os) + +### Руками +- Ремонт авто + компьютерная диагностика +- Стройка (два дома построил, вник в процесс полностью) +- Сборка мебели, ремонты +- Паяльник, электроника, электрика + +--- + +## Текущий жизненный контекст + +- Недавно бросил пить (май 2026) — мотивация: неприятные инциденты + ультиматум жены + разговор с другом Серёгой → личное решение +- **Регуляторный стек вместо алкоголя:** зал, сауна, массаж, баня+квас, б/а IPA, чтение вечером, прогулки пешком 20 мин до коворкинга +- Ходит в спортзал (рядом с коворкингом, есть сауна) +- Коворкинг в Бишкеке — структура для ADHD, разделяет работу и семью +- Работает над personal-os как системой для себя + +--- + +## Интересы + +- Home automation (Home Assistant, Zigbee, RPi) +- 3D-печать — [[3d-print-wishlist]] +- **Видеоигры:** игровой ПК собран как TV-приставка (лончер, джойстики, эмуляторы настроены). Играет редко — паттерн накопления без использования. Прогресс: прошёл с Лизой уровень Shovel Knight, запустил Witcher 3 (вводная сцена). +- Настолки: Руммикуб, Шакал, Ticket to Ride, Hive, Rush Hour, Диксит +- Совместные игры с Лизой — [[games-wishlist]] +- Геокэшинг +- Хайкинг (Алтай и окрестности) +- **Готовка:** рецепты собирает, готовит редко в одиночку. В компании / на выезде — шашлыки, куурдак, плов в казане (НГ в Сибири). Паттерн: нужен социальный контейнер. +- Кино — [[movies-watchlist]] +- Чтение — [[books-reading-list]] (широкий диапазон: философия, история, sci-fi, психология); читает вечером регулярно +- Электроника / DIY + +--- + +## Музыка + +- Закончил музыкальную школу по фортепиано +- Ходил на курсы гитары дважды (последний раз ~пол года, потом стоп); сейчас играет на 3 аккордах +- Гитара: **LAVA ME** — лежит, не используется +- Музыкальный гаджет: **Teenage Engineering Pocket Operator** — побаловался, лежит +- Хорошо поёт (каraoke — [[karaoke-songs]]) +- Сделал несколько треков сам (нужно курировать и доделывать) +- Экспериментирует с **Bulka** (AI layer over Strudel — music programming) +- Сохранены курсы (Instagram): продюсирование + импровизация на фортепиано — не начаты +- Паттерн: покупает/сохраняет → не использует без внешней структуры (→ [[psychology/profile]]) + +--- + +## Планы поездок 2026 + +| Период | Направление | Статус | +|--------|-------------|--------| +| Скоро | Алматы — Cirque du Soleil | Планируется | +| 20.07–14.11 | Новосибирск + трип на машине в Братск (к родителям Наталии) | Планируется | +| Ноябрь–март | Новосибирск (зимовка + НГ) → обратно в Бишкек | Регулярно | +| Желаемое | Турция / острова — куда вписать непонятно | Wishlist | + +--- + +## Психологическое направление + +- С психологом не работает (сессии были, но не регулярно) +- Хочет добавить в personal-os: регулярные дайджесты, проработки, анализ прогресса +- AI-психолог как постоянный компонент системы +- Идея непредвзятого агента: обсуждать Алекса "о нём" (в 3м лице), а не "обо мне" — чтобы избежать сycophancy → [[unprejudiced-ai-psychologist]] +- Искать скиллы AI-психолога для ассистентов + +--- + +## Финансы + +- Вёл детальную Excel-таблицу с мультивалютными счетами и расходами +- Последнее время не ведёт — "денег хватает" +- Открытые вопросы: инвестиционная стратегия, дополнительный доход, пенсионное планирование +- Есть идея budget-app в [[app-ideas]] и [[budget-app-features]] + +--- + +## Неизвестно / уточнить + +- [ ] ДР Наталии +- [ ] ДР Лизы (точная дата) +- [ ] Митя (одноклассник) — ДР "14.04 или около того", уточнить +- [ ] Лена Москаленко — ДР "12 сентября что ли", уточнить (попробовать через ВКонтакте) +- [ ] Школа Изобретателей — что за программа, чему учат +- [ ] Музыкальный плейлист Лизы — добавить в её профиль