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