--- namespace: work tags: [system, self-modification, eagle, meta] last_updated: 2026-05-14 confidence: 1.0 --- # Personal OS — Self-Modification Guide How Eagle (and Alex via Claude Code) can safely evolve the system without breaking it. **Rule**: Always show a diff/draft first, confirm, then apply. Commit the vault after. --- ## What Eagle Can Change (no approval needed — show draft first) ### 1. Prompt files (`~/Developer/personal-os/agent/prompts/*.md`) Eagle reads and writes its own prompts. When Alex says "add X to the morning brief" or "change how inbox triage handles Y": 1. Read the current prompt: `~/Developer/personal-os/agent/prompts/.md` 2. Draft the change, show the diff in Zulip 3. On confirmation: write the file 4. Commit: `cd ~/Developer/personal-os && git add agent/prompts/.md && git commit -m "[YYYY-MM-DD] prompt: "` **Do not** change `executor-bug-fix.md` without explicit confirmation — it controls autonomous code changes. ### 2. SOUL.md (`~/.hermes/SOUL.md`) Eagle's identity, rules, and operating context. Eagle can propose changes to: - Allowed vault write paths - Focus gating rules - Channel routing Same flow: draft → confirm → write → no vault commit needed (SOUL.md is outside vault). ### 3. ActivityWatch project rules (`~/Developer/personal-os/aw-projects.json`) To classify a new project as work or personal: 1. Read current `aw-projects.json` 2. Add entry with `project`, `namespace` (omit for work, `"personal"` for pet projects), and `rules` 3. Show draft, confirm, write 4. Commit: `cd ~/Developer/personal-os && git add aw-projects.json && git commit -m "[YYYY-MM-DD] aw: classify "` The `namespace: "personal"` field excludes a project from work activity in status.md and activity_daily. ### 4. Vault knowledge pages (`~/obsidian/wiki/`, `~/obsidian/work/wiki/`) Eagle does **not** write to `wiki/` — that's the wiki-ingest job (22:00 launchd). But Eagle **can** write to: - `~/obsidian/work/projects/SLUG.md` — project notes - `~/obsidian/work/decisions/YYYY-MM-TOPIC.md` — decisions - `~/obsidian/personal/` — personal notes - `~/obsidian/family/` — family docs After writing: `cd ~/obsidian && git add -A && git commit -m "[YYYY-MM-DD] "` ### 5. Correction logging (corrections_log table) Eagle silently logs a row whenever Alex pushes back on a suggestion. No approval needed — this is continuous background instrumentation, not a visible change. ```sql INSERT INTO corrections_log (date, week_number, source, original_plan, correction, deferred_gids, reason_tag) VALUES (current_date, EXTRACT(WEEK FROM current_date)::int, '', '', '', ARRAY[]::text[], ''); ``` --- ## What Requires Approval (always confirm explicitly) ### Schema changes (PostgreSQL) New tables, columns, or indexes require a SQL migration. Pattern: 1. Draft the SQL in Zulip (or Mattermost, post-migration), 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]] — full system map - [[personal-os-agent-rules]] — Eagle's rules and channel routing - [[personal-os-purpose]] — why this system exists