A behavioral operating system where an LLM does the reasoning and a small Python backend owns identity config, scoring, and memory. It turns daily task lists into one prioritized objective per day, gated by measured energy and sleep.
- Scores any list of tasks against a weighted value hierarchy (vitality 10, discipline 8, relationships 6, building 5, learning 4) defined in
config/identity.json, plus time pressure, priority, and goal linkage boosts. - Derives a daily operating mode (RECOVERY, BUILD, or SPRINT) from the last 7 days of reflection data instead of letting the user pick one. Hard gates force RECOVERY when average energy is below 5, energy trend is falling, or sleep quality is below 5.
- Caps recommendations by mode: RECOVERY allows 2 tasks, BUILD allows 4, SPRINT allows 5. The scorer returns one "One Thing" plus support tasks, never an open-ended list.
- Persists morning briefings, evening reflections, weekly reviews, and goal check-ins to SQLite (
data/operator.db), then computes 7-day averages, an energy trend, and plain-language insights. - Tracks measurable goals from
config/goals.jsonwith check-in history, progress percent, days remaining, and a velocity estimate (average change per check-in over the last 5 check-ins). - Regenerates a goal dashboard in an Obsidian vault after every reflection and goal check-in (
vault_sync.py), failing silently if the vault is absent.
Task managers answer "what is due". They do not answer "what should a person with depleted energy actually attempt today". LifeOS inverts the usual setup: the LLM (Claude, following the runbook in OPERATOR.md) fetches calendar and reminder data, asks the reflection questions, and writes the briefing, while the Python layer holds the parts that must be deterministic and auditable: the scoring math, the mode gates, and the historical record.
The live system is operator.py + memory/store.py, driven by an LLM that reads OPERATOR.md as its runbook. Calendar and reminder data enter on the LLM side via MCP, not through Python.
flowchart TD
OP[OPERATOR.md runbook] --> C[Claude: reasoning, data fetch, presentation]
CAL[Calendar and reminders via MCP] --> C
C -->|CLI calls| CLI[operator.py]
ID["config/identity.json<br/>weights, rules, modes"] --> CLI
G["config/goals.json<br/>active goals"] --> CLI
CLI --> ST["memory/store.py<br/>pattern analysis, mode gates"]
ST --> DB[("data/operator.db<br/>daily_logs, briefings,<br/>weekly_reviews, goal_checkins")]
CLI -->|after reflect / goal-checkin| VS[vault_sync.py] --> OBS[Obsidian dashboard]
ST -->|7-day patterns + mode| C
Scoring in operator.py::score_tasks is additive: keyword-to-category matches earn the category weight, multiplied by 1.5 when the category is in the current mode's focus areas. Overdue adds 5, due today adds 3, due this week adds 1. High priority adds 4, medium adds 2. An explicit goal_id adds 3. When average energy is below the low threshold, non-vitality tasks lose 2 points and vitality tasks gain 3. Tasks that match nothing score 1 as a floor.
./setup.sh # creates .venv (prefers uv), installs requirements.txt
source .venv/bin/activate
python3 operator.py status # identity, derived mode, patterns, active goals
python3 operator.py patterns # 7-day pattern analysis + mode recommendation
python3 operator.py score-tasks '{"tasks": [{"title": "30 minute walk", "due": "today", "priority": "medium"}, "Draft launch checklist"]}'
python3 operator.py reflect '{"energy": 6, "sleep_quality": 7, "food_compliance": 8, "one_thing": "Ship the demo", "one_thing_done": 1, "mood": "steady"}'
python3 operator.py goal-checkin '{"goal_id": "example-goal", "value": 3, "note": "weekly count"}'
python3 operator.py ceo-review # generate + persist the weekly review
python3 operator.py dashboard # regenerate the vault dashboard manuallyPython is pinned to 3.14.3 (.python-version). Dependencies are 4 packages (requests, python-dotenv, twilio, flask); the current CLI path uses none of the network-facing ones.
- Config over code. All scoring behavior lives in
config/identity.json: weights, keyword maps, energy penalties, mode definitions, and hard rules. Changing how tasks rank never requires touchingoperator.py. - Mode is derived, not chosen.
determine_mode()reads 7 days of logged data and applies hard gates. Notably, the function only ever returns RECOVERY or BUILD; SPRINT is defined in config but no code path selects it automatically. Entering SPRINT is a manual, deliberate act. - SQLite with deliberate constraints.
daily_logs.dateis UNIQUE and reflections use INSERT OR REPLACE, so re-reflecting on a day overwrites rather than duplicates. The schema is idempotent (CREATE TABLE IF NOT EXISTS) and re-runs on every CLI call.journal_mode=OFFis set intentionally for compatibility with mounted filesystems. - The LLM never touches the database directly. Every write goes through a CLI command with a defined JSON shape, which keeps the memory layer testable and the LLM replaceable.
Active. The v2 core (operator.py, memory/store.py, OPERATOR.md) is in daily use. A pre-v2 implementation (briefing.py, reflection.py, operator_loop.py, and the agents/, ceo/, integrations/ packages) remains in the tree but imports store functions that no longer exist and will crash on import; it is kept as history, not as working code. There are no automated tests; this is a single-user tool whose correctness is checked by daily operation.