使用说明
Personal AI Employee — Gold Tier
Your life and business on autopilot. Local-first, agent-driven, human-in-the-loop.
Tier: Gold | Built with Claude Code + Obsidian + Python + Node.js MCP
A Digital FTE (Full-Time Equivalent) AI agent that autonomously manages email, WhatsApp, LinkedIn, Twitter/X, Facebook, Instagram, Odoo ERP accounting, and business tasks — 24/7, with human approval for all sensitive actions.
Architecture
A closed loop: the system perceives external events, remembers them as markdown, reasons over them with Claude, asks a human before anything sensitive, acts through MCP servers, and records every step back into memory.
flowchart TB
S["Gmail · WhatsApp · LinkedIn · Twitter/X<br/>Facebook/Instagram · Bank · Filesystem"]
W["Perception Layer — 7 Python Watchers<br/><i>base_watcher.py: polling · dedupe · backoff</i>"]
V["Memory — Obsidian Vault<br/><i>markdown + frontmatter</i><br/>Tasks · Plans · Handbook · Accounting"]
O["orchestrator.py<br/><i>dispatch · HITL routing · watchdog</i>"]
C["Claude Code + 10 Agent Skills<br/><i>reason → plan → act</i>"]
H["stop_hook.py<br/><i>Ralph loop — keeps working<br/>until the task is actually done</i>"]
G{"Human Approval Gate<br/>sensitive actions only"}
M["Action Layer — 5 MCP Servers<br/>email · linkedin · twitter · facebook · odoo"]
A["audit_logger.py<br/><i>90-day retention</i>"]
R["retry_handler.py<br/><i>exponential backoff<br/>+ graceful degradation</i>"]
D["Web Dashboard<br/><i>localhost:8765</i>"]
S --> W --> V --> O --> C
C <--> H
C --> G
G -->|approved| M
G -->|rejected| V
M --> R --> A
A --> V
V --> D
classDef src fill:#0d1117,stroke:#2f81f7,color:#c9d1d9
classDef mem fill:#0d1117,stroke:#8957e6,color:#c9d1d9
classDef gate fill:#0d1117,stroke:#d29922,color:#f0d58a,stroke-width:2px
classDef act fill:#0d1117,stroke:#3fb950,color:#c9d1d9
classDef obs fill:#0d1117,stroke:#6e7681,color:#c9d1d9
class S,W src
class V,O,C,H mem
class G gate
class M act
class A,R,D obs
The loop that matters: nothing reaches the outside world without passing the approval gate, and every action that does pass is written back into the vault. The agent's memory and its audit trail are the same artifact — which is why you can read what it did in plain markdown, and why a rejected action teaches it just as much as an approved one.
Design Decisions
Five choices shaped this system. Each one bought something and cost something.
1 · The filesystem is the state machine
Chosen: a task is a markdown file, and its state is which folder it sits in.
Needs_Action -> In_Progress -> Pending_Approval -> Approved / Rejected -> Done.
Given up: transactions, queries, concurrent writers, and anything resembling a
schema. There is no database, so there is no SELECT.
Why: because it makes the approval gate mv. Approving an action is moving a file
from Pending_Approval to Approved — which works in Obsidian, in Finder, over SSH,
or on a phone with a synced folder. No custom approval UI has to exist, and none can
break. The dashboard at localhost:8765 is a reader of that state, not the owner of
it; delete the dashboard and the system still works.
The cost is real: two watchers writing the same task at the same moment would race, and there is no migration path if the frontmatter shape changes. At single-operator scale that trade is worth it. At team scale it would not be.
2 · Approval is a gate, not a notification
Chosen: sensitive actions stop at Pending_Approval and stay there. orchestrator.py
only dispatches out of Approved, so nothing reaches the outside world without a human
having physically moved the file.
Given up: speed. The agent cannot finish an email at 3 AM; it can only prepare one.
Why: an agent with your Gmail token, your LinkedIn account and your Odoo ledger is not a productivity tool — it is a liability surface. The failure mode of "notify the human afterwards" is an apology to a client. The failure mode of "wait for the human" is a delay. Delays are recoverable.
DRY_RUN defaults to true in orchestrator.py and independently inside all five MCP
servers. That duplication is deliberate: a bug in the orchestrator still cannot cause a
real send, because the server it calls is also refusing. Two locks, one key — and CI
asserts that every server still has its lock, because two of them once did not.
3 · Actions go through MCP servers, not inline API calls
Chosen: each integration is a separate Node process speaking MCP. orchestrator.py
holds a routing table (action_type -> server + skill) and shells out.
Given up: simplicity and latency. Five npm installs, five processes, and a
subprocess hop that an inline requests.post() would not need.
Why: credential isolation and blast radius. The Twitter server only ever receives Twitter credentials; a bug in it cannot touch the Odoo ledger. Each server is testable alone, replaceable alone, and usable by Claude Code directly without the orchestrator running at all. When an integration breaks, exactly one process is suspect.
Unknown action_type values fall back to a generic Claude prompt rather than raising —
an unrecognised action degrades into "a human-readable instruction" instead of a crash.
4 · Memory is markdown, and it is the same artifact as the audit trail
Chosen: the vault is plain markdown with YAML frontmatter, readable in Obsidian. The agent's working memory and its record of what it did are one directory tree.
Given up: query performance, and any guarantee that a human editing the vault mid-run will not confuse the agent.
Why: you can audit it by hand. When an autonomous agent has acted on your behalf for a week, the question is never "what does the database say" — it is "show me what it actually did, in a form I can read without your tooling." A JSON audit log in Postgres answers that badly; a folder of dated markdown answers it in a text editor, five years from now, with no dependency on this project still existing.
Retention is 90 days by default and purge_old_logs() only deletes files matching
YYYY-MM-DD.json — audit_summary.md and the health state survive a purge on purpose.
5 · Watchers poll; they do not receive webhooks
Chosen: each of the seven watchers polls on an interval, inheriting the retry-safe
loop in base_watcher.py.
Given up: latency. A webhook is instant; a 30-second poll averages 15 seconds behind.
Why: webhooks require a public endpoint, which requires a server, a domain, TLS, and an inbound hole in the network. This system is designed to run on the operator's own machine with no inbound connectivity at all. Polling is the price of that, and 15 seconds is not a meaningful delay for triaging an inbox.
The loop's guarantee matters more than its latency: one failing poll never kills the
watcher. base_watcher.run() catches per-cycle exceptions and continues, and
tests/test_base_watcher.py asserts exactly that. A watcher that dies silently is worse
than one that never started, because the operator believes it is still running.
Silver Tier is the same architecture with a smaller surface — 4 watchers, 2 MCP servers, 7 skills, no audit logger and no retry handler: SirHamza/silver-tier-complete.
These are the trade-offs as the code makes them. Where a decision no longer fits — team scale, higher volume, an integration that genuinely needs webhooks — the section above is also the list of what to revisit first.
What It Looks Like Running
The dashboard reads the Obsidian vault directly. Every number above comes from markdown files on disk — there is no database and no hidden state, which is what makes the agent's memory auditable by hand.
Full view — activity log, active plans, briefings and the 10 agent skills
Note the two post_linkedin rows in the activity log: a 429 rate limit marked error,
followed by recovered on attempt 3 marked success. That is orchestrator/retry_handler.py
doing exponential backoff — the failure is recorded rather than hidden, which is the whole
point of the audit trail.
Reproduce this yourself in under a minute — see Quick Start below. Screenshots use sample data from
demo/seed_demo.py; no credentials involved.
Gold Tier Features
| Feature | Implementation |
|---|---|
| Full cross-domain integration | Personal (email, WhatsApp) + Business (social, accounting) |
| Odoo Community ERP | mcp_servers/odoo_mcp/ — JSON-RPC invoicing + accounting |
| Facebook/Instagram | mcp_servers/facebook_mcp/ + watchers/facebook_watcher.py |
| Twitter/X | mcp_servers/twitter_mcp/ + watchers/twitter_watcher.py |
| Finance Watcher | Bank CSV drops + API polling → alerts for large transactions |
| Ralph Wiggum Stop Hook | hooks/stop_hook.py + .claude/settings.json |
| Audit Logging (90-day) | orchestrator/audit_logger.py — JSON logs + markdown summary |
| Error Recovery | orchestrator/retry_handler.py — exponential backoff + graceful degradation |
| 5 MCP Servers | email, linkedin, twitter, facebook, odoo |
| 10 Agent Skills | 7 Silver + post-twitter, post-facebook, odoo-accounting |
Quick Start
Three levels. Level 0 needs nothing but Python and takes under a minute.
Level 0 — Demo mode (no credentials, no cost)
git clone https://github.com/SirHamza/gold-tier.git && cd gold-tier
pip install python-dotenv
python demo/seed_demo.py # sample vault data, clearly marked
python dashboard/server.py # http://localhost:8765
Nothing here touches a network service or needs an API key. Remove the sample
data with python demo/seed_demo.py --clean.
Level 1 — Check what you have
python scripts/check_setup.py
Reports which runtimes, packages and credential groups are present, and tells you what demo and live mode can currently do. It never prints a secret value, only whether one is set.
Level 2 — Live mode
Prerequisites
| Need | Install |
|---|---|
| Python 3.10+ | pip install -r requirements.txt |
| Node.js 18+ | required by the five MCP servers |
| Claude Code | npm install -g @anthropic/claude-code |
| PM2 (optional) | npm install -g pm2 — process supervision |
| Odoo 16+ (optional) | self-hosted, for the accounting skill |
pip install -r requirements.txt
for d in email linkedin twitter facebook odoo; do
(cd "mcp_servers/${d}_mcp" && npm install)
done
Configure
cp .env.example .env
Two variables matter before anything else:
# Absolute path to this clone. The five MCP servers resolve their entry
# points from it, so nothing starts until this is correct.
AI_EMPLOYEE_ROOT=/absolute/path/to/gold-tier
# Safety switch. While true, every outbound action is logged but NOT sent.
# Leave it on until you have watched a full cycle in the dashboard.
DRY_RUN=true
Everything else is optional and independent. Configure only the channels you want — an unconfigured watcher stays idle rather than failing, so you can start with Gmail alone and add the rest later.
Run
pm2 start scheduling/pm2.config.js && pm2 save # supervised
# or run pieces by hand:
python orchestrator/orchestrator.py
python watchers/gmail_watcher.py
python dashboard/server.py
Then open http://localhost:8765 and re-run check_setup.py to confirm the
feature groups you configured now report ready.
Agent skills
claude /process-inbox # triage everything in Needs_Action
claude /ceo-briefing # morning business briefing
claude /send-email # dispatch an approved email
claude /post-linkedin "Your post text"
claude /post-twitter "Tweet text"
claude /post-facebook "Post text"
claude /odoo-accounting summary
claude /create-plan "Task description" # turn a task into PLAN_*.md
claude /weekly-audit # revenue + subscription + Odoo audit
claude /ralph-loop "<task>" --completion-promise "DONE" --max-iterations 10
Scheduled tasks (Windows)
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
.\scheduling\setup_windows_scheduler.ps1
# CEO Briefing (Mon-Fri 8am) + Weekly Audit (Sun 11pm)
The script resolves the project root from its own location, so it works wherever
you cloned the repo. macOS and Linux use scheduling/setup_cron.sh.
Tests
pip install -r requirements-dev.txt
python -m pytest
43 tests covering the three files the whole system leans on:
| Module | What is asserted |
|---|---|
orchestrator/retry_handler.py |
exponential backoff schedule and its max_delay cap, that PermanentError is never retried, that MaxRetriesExceeded fires only after the last attempt, and that GracefulDegradation falls back instead of raising |
orchestrator/audit_logger.py |
entry schema, one file per day, the approval trail records request and approval as two entries, and that retention purges dated logs without touching audit_summary.md or the health state |
watchers/base_watcher.py |
the abstract contract, that Needs_Action is always created, and that a failing poll never kills the loop — the guarantee all seven watchers depend on |
Retry tests patch time.sleep and assert the recorded delay schedule instead of
waiting on it, so the whole suite runs in well under a second.
tests/test_module_imports.pyexists because of a real bug:retry_handler.pycalledos.getenv()at module scope whileimport ossat twenty lines below it. The module raisedNameErroron import, and becauseorchestrator.pyguards that import withexcept ImportError— which does not catchNameError— the orchestrator crashed on startup instead of degrading. An import is the cheapest test there is.
Project Structure
AI_Employe/
├── AI_Employee_Vault/ # Obsidian vault (GUI + long-term memory)
│ ├── Dashboard.md # Real-time status
│ ├── Company_Handbook.md # AI rules of engagement
│ ├── Business_Goals.md # KPIs and revenue targets
│ ├── Needs_Action/ # Items awaiting Claude
│ ├── Plans/ # PLAN_*.md + RALPH_STATE_*.json
│ ├── Pending_Approval/ # Actions awaiting human approval
│ ├── Approved/ # Move files here to authorize
│ ├── Rejected/ # Move files here to cancel
│ ├── Done/ # Completed task archive
│ ├── Logs/ # JSON audit trail (90-day retention)
│ ├── Briefings/ # Monday CEO briefings
│ ├── Content_Queue/ # Social media post drafts
│ ├── Finance_Drop/ # Drop bank CSVs here for processing
│ └── Accounting/ # Financial data + Odoo sync
├── watchers/ # Python perception scripts (× 7)
│ ├── base_watcher.py # Abstract base class
│ ├── gmail_watcher.py # Gmail API
│ ├── whatsapp_watcher.py # Playwright WhatsApp Web
│ ├── linkedin_watcher.py # LinkedIn API
│ ├── filesystem_watcher.py # Local file drops
│ ├── finance_watcher.py # Bank CSV + API (Gold)
│ ├── twitter_watcher.py # Twitter mentions + keywords (Gold)
│ └── facebook_watcher.py # Facebook/Instagram comments (Gold)
├── orchestrator/ # Master process
│ ├── orchestrator.py # Folder watcher + MCP dispatcher
│ ├── watchdog.py # Process health monitor
│ ├── audit_logger.py # 90-day audit logging (Gold)
│ └── retry_handler.py # Exponential backoff + degradation (Gold)
├── mcp_servers/ # External action servers (× 5)
│ ├── email_mcp/ # Gmail OAuth2 (send, draft, list)
│ ├── linkedin_mcp/ # LinkedIn REST API
│ ├── twitter_mcp/ # Twitter API v2 (Gold)
│ ├── facebook_mcp/ # Facebook Graph API + Instagram (Gold)
│ └── odoo_mcp/ # Odoo JSON-RPC ERP (Gold)
├── hooks/ # Claude Code hooks (Gold)
│ └── stop_hook.py # Ralph Wiggum Stop Hook
├── dashboard/ # Web Dashboard UI
│ ├── server.py # Python HTTP server + JSON API
│ └── index.html # Real-time web dashboard
├── skills/ # Agent Skill descriptors (× 10)
│ ├── process-inbox.md
│ ├── ceo-briefing.md
│ ├── post-linkedin.md
│ ├── post-twitter.md # Gold
│ ├── post-facebook.md # Gold
│ ├── send-email.md
│ ├── create-plan.md
│ ├── weekly-audit.md
│ ├── ralph-loop.md
│ └── odoo-accounting.md # Gold
├── .claude/
│ ├── commands/ # Claude Code slash commands (× 10)
│ └── settings.json # Stop hook registration (Gold)
├── scheduling/
│ ├── pm2.config.js # PM2 (all 10 processes)
│ └── setup_windows_scheduler.ps1
├── .mcp.json # 5 MCP server registrations
├── .env.example # Full credential template
└── PLAN.md # Development roadmap (Gold complete)
Human-in-the-Loop (HITL) Workflow
1. Claude reads Needs_Action file
2. Claude creates: Pending_Approval/APPROVAL_*.md
3. Review in Obsidian or Dashboard UI (http://localhost:8765)
4. Move to /Approved → Orchestrator dispatches via correct MCP server
Move to /Rejected → Action cancelled, logged
Actions that ALWAYS require approval:
- Emails to new contacts / bulk sends
- LinkedIn / Twitter / Facebook / Instagram posts
- Odoo invoice confirmation (posting to ledger)
- Payments > $50 or any new payee
- Finance alerts flagged by Finance Watcher
Ralph Wiggum Stop Hook
The stop hook (hooks/stop_hook.py) intercepts Claude's exit signal:
Claude starts a ralph-loop task
→ Creates RALPH_STATE_<id>.json in /Plans
→ Works on task
→ Tries to exit
→ Stop hook checks: is task file in /Done?
YES → Allow stop (complete)
NO → Block exit, increment iteration, Claude continues
→ Max iterations → creates INCOMPLETE_<task>.md alert
Registration: .claude/settings.json → hooks.Stop
Odoo Community ERP Integration
Odoo (local, http://localhost:8069)
↕ JSON-RPC 2.0
Odoo MCP Server (mcp_servers/odoo_mcp/)
↕
Claude Code + /odoo-accounting skill
Tools: create_invoice, confirm_invoice (HITL),
create_customer, list_invoices, list_products,
get_accounting_summary (month-by-month P&L)
Weekly audit automatically pulls Odoo accounting summary for CEO Briefing.
Audit Logging
Every action is logged to Vault/Logs/YYYY-MM-DD.json:
{
"timestamp": "2026-02-20T10:30:00",
"action_type": "send_email",
"actor": "claude",
"target": "client@example.com",
"parameters": {"subject": "Invoice #123"},
"approval_status": "approved",
"approved_by": "human",
"result": "success"
}
- 90-day retention —
audit_logger.purge_old_logs()auto-runs weekly - Weekly summary →
Vault/Logs/audit_summary.md
Security
| Measure | Implementation |
|---|---|
| Credentials | .env file (gitignored), never in vault |
| Default mode | DRY_RUN=true — no real actions until you're ready |
| Payments | Always require human approval, never auto-execute |
| Odoo write ops | All invoice posting / customer creation requires approval |
| Audit trail | 90-day JSON logs in Vault/Logs/ |
| Rate limiting | Max 10 emails/hr, max 3 payments/day |
| Sessions | WhatsApp session in sessions/ (gitignored) |
| Finance alerts | Large transactions (>$200) → Needs_Action alert |
Where This Sits
Four capability tiers, built in order. Each one is a working system on its own, not a stage of an unfinished one.
| Tier | Scope | Status |
|---|---|---|
| Bronze | Vault + watchers + Claude reading and writing markdown | shipped |
| Silver | 4 watchers · 2 MCP servers · HITL approval · dashboard | shipped |
| Gold (this repo) | 7 watchers · 5 MCP servers · Odoo ERP · audit logging · retry + graceful degradation · Ralph loop | shipped |
| Platinum | Cloud/local split, always-on hosting, agent-to-agent delegation | not started |
Silver is the one to read first if you want the architecture without the integration surface. Gold is the one to read if you want to see how it holds up once there are five external services, an ERP ledger and a 90-day audit trail involved.
Author
Hamza Saifuddin — AI Architect · Agentic Systems · Karachi, PK
Portfolio · GitHub · LinkedIn · hamzasaifuddinh@gmail.com
Licensed under MIT — use it, fork it, build on it.
