--- namespace: work tags: [system, architecture] last_updated: 2026-05-18 confidence: 1.0 --- # Personal OS — Architecture ## Why It Exists See: [[personal-os-purpose]] and [[personal-os-agent-rules]] for goals and behavioral constraints. ## 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: 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), commit-vault (23:00 daily), wiki-curation (02: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 - **Zulip plugin**: `~/.hermes/plugins/zulip-topic-routing/` — intercepts `(no topic)` messages; routes to most recent related thread or renames to descriptive topic. Status: ✅ done (2026-05-14) > ⚠️ **Mattermost migration in-progress (2026-05-14):** Work Eagle agent (briefs, inbox, executor) moving from Zulip → corporate Mattermost. Personal/family/journal remain on Zulip/Discord. Blocked on IT Ops creating MM bot accounts (eagle-bot, executor-bot, brief-bot). See `work/plans/mattermost-migration.md`. ### Ретроспектор (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 > ⚠️ Stale as of 2026-05-11: Original LLM-orchestrated autonomous cron **paused**. Replaced by v2 bash daemons: **Autonomous mode (v2 — bash daemons, active 2026-05-11+):** - `executor-runner.sh` — cron `*/5`; processes queued/in_progress/pr states; max 2 concurrent workers; spawns focused worker prompt per state; exits when no actionable tasks remain - `executor-analyzer.sh` — cron `*/5`; picks one unanalyzed task, runs `claude -p` analysis prompt, sets `for_review`/`awaiting_go`/`skip`, exits; restarts for next task - Worker prompts in `~/Developer/personal-os/agent/prompts/executor-worker-*.md` - Queue state in `executor_queue` table; live plan: `~/Developer/personal-os/briefs/executor-queue.md` **Orchestrator Pattern (Planning — 2026-05-14):** > Research phase complete. Executor will become a separate Hermes profile with its own Zulip bot identity ("Исполнитель"), spawned by Eagle as orchestrator. Eagle monitors `#executor` via 2-min cron, auto-approves safe actions (`[REQUEST:create_worktree]`, `[REQUEST:run_tests]`, etc.), escalates ambiguous/risky ones to Alex. See `personal/projects/personal-os/executor-spawning-research.md` for full protocol and auto-approval rules. > > Key mechanism: **Hermes Profiles** — `~/.hermes/profiles/executor/` with its own SOUL.md, Zulip bot token, and gateway. `hermes profile create executor --clone`. **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 **Eagle (Mac M4) — Hermes agent:** | Name | Command | Used for | |------|---------|---------|| | obsidian | `node ~/scripts/obsidian-mcp-wrapper.js` (wraps `/opt/homebrew/bin/obsidian-mcp`) | Vault search, read, write. Wrapper handles ZodError/race condition — see [[obsidian-mcp-wrapper]] | **Taiga (TrueNAS) — Hermes agent (`hermes-taiga` container):** | Name | Command | Used for | |------|---------|---------|| | obsidian | `/opt/data/.local/bin/mcpvault /vault` | Vault access (sparse: personal/ + family/). Tavily search enabled. | **Kraken (RPi5) — Hermes agent (`hermes-kraken` container):** || Name | Command | Used for | ||------|---------|---------|| || obsidian | `/opt/data/.local/bin/mcpvault /vault` | Vault access (sparse: personal/ + family/). Tavily search enabled. | Skills on Kraken: `~/.hermes/skills/` (host) — **not** visible to the container at `/opt/data/skills/`. Copy skills there if needed. Media stack on Kraken: full *arr stack (Prowlarr/Radarr/Sonarr) + Transmission + Jellyfin + media-pipeline. See [[concepts/kraken-media-stack]]. > ⚠️ **Kraken image routing (open issue, 2026-05-14):** Gemini API credits depleted → fallback to OpenRouter models (gpt-oss-120b, minimax-m2) → these lack vision → image inbox messages fail. Desired fix: add `save_to_fs` mode to Hermes `image_routing` (save image locally, pass path to agent instead of bytes). Workaround: top up Gemini API credits on Kraken. Tracked in `personal/projects/personal-os/kraken-obsidian-write-bug.md`. > All three nodes run Hermes with obsidian/mcpvault MCP. Eagle uses obsidian-mcp-wrapper.js (fixes ZodError+race); Taiga and Kraken use mcpvault directly. **Claude Code / `claude -p` sessions (Eagle only):** | Name | Command | Used for | |------|---------|---------|| | ddg-vm | `npx tsx ~/Developer/virfield/server/mcp-server.ts` | VM lifecycle for UI tests | | obsidian | `node ~/scripts/obsidian-mcp-wrapper.js` | 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/ ```