[2026-05-29] wiki/ cleanup: move files to vault, drop stale duplicates

- wiki/tech/ → family/how-to/ (15 files, no duplicates existed)
- wiki/{concepts,comparisons,root}/ → personal/projects/personal-os/ (22 files)
- wiki/tech/media-pipeline-pitfalls.md → personal/projects/
- wiki/tech/reflect-skip-fuse.md → personal/projects/
- wiki/ddg-asana-workflow.md → work/
- wiki/user-profile.md → personal/
- Dropped 11 files: vault versions are newer/bigger (xgimi, htpc-steam,
  vault-strategy, executor-orchestrator, executor-security-incident,
  kraken-media-stack, library-app, psychologist-app, raw/processed/, log, index)

4 files pending review (wiki version larger than vault original):
htpc-magic4pc, tv-luna-send, wireguard-vpn, watchlist-automation
This commit is contained in:
Alexey Martemyanov
2026-05-29 15:27:36 +06:00
parent 821a9df71b
commit 2ccb7e0a4a
52 changed files with 0 additions and 1046 deletions
@@ -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.