1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176 | # CUTOVER โ sovereign-org-engine Telegram โ chairman-agent bridge
Consolidates @Wazier_bot into the Chairman. One brain (agent.py + CHAIRMAN-MEMORY.md),
every channel (iMessage webhook, heartbeat, now Telegram). Drafted 2026-07-04. Nothing
in this document has been executed โ no plists created, no services touched.
## What moved
| org-engine (old) | chairman-agent (new) |
|---|---|
| `bot.py` + python-telegram-bot polling | `telegram_bridge.py` โ stdlib long-poll, same mode |
| `core/orchestrator.py` (own Claude client, own tool set) | `agent.py:agent_loop()` โ Chairman's existing decision path + tools |
| `config/agents.py` 32-agent roster | copied verbatim to `chairman-agent/config/agents.py` |
| `core/cost_guard.py` (monthly caps, 80% auto-cutoff) | ported to `chairman-agent/cost_guard.py` โ same caps/threshold/state schema; real usage metered by wrapping `agent.api_call` |
| `core/supabase_bus.py` writes (task ledger, decisions, reports) | ported to `chairman-agent/agent_bus.py` โ same tables/project, stdlib urllib, fail-soft |
| `/pulse` `/costs` `/update` `/musa` `/unlock` handlers | ported into the bridge, plain-text formatting |
| Mansu Musa AUTONOMOUS cycle | retired; `/musa audit\|agents\|leaks\|brief` runs on demand via the MANSU_MUSA persona |
| `agent-memory/mempal.md` | NOT carried โ Chairman memory is the single memory (see ยง6) |
| crash on network loss (exit 1, KeepAlive loop) | retry-forever with backoff, 409-conflict aware |
Cost state lives in `chairman-agent/data/api_costs.json` (same JSON schema as
org-engine's `data/api_costs.json`). To carry this month's spend across the cutover,
copy the org-engine file to that path before first launch โ optional; a fresh file
starts the month at $0. While the cutoff is tripped, API-calling routes are refused
with a notice; `/costs` `/pulse` `/update` `/status` `/agents` `/unlock` still work.
Routing semantics preserved: plain text goes to the Chairman who delegates to roster
personas (creative chain Philosophy CEO โ Taste Manager โ Brand Designer โ production
is enforced in the system addendum); `/agent AGENT_ID task` runs a persona charter
directly; `/agents` lists the roster. Verified live 2026-07-04 via `--test` โ the
Chairman answered with the correct Philosophy-CEO-first routing.
## 1. Pre-flight (no service impact)
1. Telegram creds: `TELEGRAM_BOT_TOKEN` + `TELEGRAM_USER_ID` currently live only in
`sovereign-org-engine/config/.env`. The bridge falls back to reading that file, so
cutover works with zero env surgery โ but **copy both lines into
`content-os/config/.env`** so the org-engine directory can eventually be archived
without breaking the bridge.
2. Offline checks (already passing as of drafting):
```
cd ~/sovereign-empire-sweep/chairman-agent
python3 telegram_bridge.py --roster # 32 agents, grouped by tier
python3 telegram_bridge.py --test "ping" # full agent_loop round-trip, no Telegram
```
`--test` never contacts Telegram, so it is safe while org-engine still owns the bot.
## 2. Cut over (order matters โ one poller per bot token)
Telegram allows exactly ONE `getUpdates` consumer per token. The bridge will log
`409 Conflict` and wait if org-engine is still polling โ nothing breaks, but it won't
receive messages until org-engine is gone.
1. **Stop org-engine** (frees the token):
```
launchctl bootout gui/$(id -u)/com.sovereign.org-engine
```
KeepAlive dies with the job; confirm with
`launchctl list | grep org-engine` (should be empty) and
`pgrep -f sovereign-org-engine/bot.py` (no PID).
2. **Manual soak test** โ run the bridge in a foreground shell first:
```
cd ~/sovereign-empire-sweep/chairman-agent && python3 telegram_bridge.py
```
From the phone, send to @Wazier_bot: `/start`, `/agents`, `/status`, `/pulse`,
`/costs`, `/update`, a plain message, `/musa brief`, and
`/agent RESEARCH_AGENT summarize today's AI headlines`.
Watch `logs/telegram.log` (bridge ops) and `logs/telegram-chat.log` (transcripts).
Note: messages sent during the gap between steps 1 and 2 queue server-side and
deliver when the bridge starts โ expected, not a bug.
3. **Create the LaunchAgent** (describe only โ create it by hand at cutover time):
`~/Library/LaunchAgents/com.chairman.telegram.plist` with
- Label `com.chairman.telegram`
- ProgramArguments: `/opt/homebrew/bin/python3`,
`/Users/19keys/sovereign-empire-sweep/chairman-agent/telegram_bridge.py`
- WorkingDirectory `/Users/19keys/sovereign-empire-sweep/chairman-agent`
- RunAtLoad true, KeepAlive true, ThrottleInterval 30
- StandardOutPath/StandardErrorPath โ
`/Users/19keys/sovereign-empire-sweep/chairman-agent/logs/telegram-launchd.log`
(Mirrors com.chairman.webhook.plist; no PYTHONPATH needed โ the bridge sets its own
sys.path.)
4. Ctrl-C the manual run from step 2, then:
```
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.chairman.telegram.plist
```
Re-run the phone smoke test.
5. **Retire org-engine's plist** so a reboot doesn't resurrect it:
```
launchctl disable gui/$(id -u)/com.sovereign.org-engine
mv ~/Library/LaunchAgents/com.sovereign.org-engine.plist \
~/sovereign-empire-sweep/_Archive/com.sovereign.org-engine.plist
```
Leave `sovereign-org-engine/` on disk untouched until ยง6 is done and a week of
bridge uptime has passed.
## 3. Rollback (any point, ~1 minute)
```
launchctl bootout gui/$(id -u)/com.chairman.telegram # or Ctrl-C the manual run
mv ~/sovereign-empire-sweep/_Archive/com.sovereign.org-engine.plist ~/Library/LaunchAgents/
launchctl enable gui/$(id -u)/com.sovereign.org-engine
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.sovereign.org-engine.plist
```
org-engine code and its .env were never modified, so this is a clean restore. The two
systems track Telegram offsets independently (bridge: `logs/.telegram_offset`); messages
already consumed by one will not replay to the other โ acceptable single-user loss.
## 4. Post-cutover verification
- `tail -f chairman-agent/logs/telegram.log` โ inbound/replied lines, no 409s.
- Pull the network for 60s mid-session: bridge must log `poll failed ... retry` and
recover, not exit (this is the org-engine crash it replaces).
- `launchctl list | grep chairman` โ telegram job present, last exit 0.
- Ledger freshness: after one plain message + one `/agent` run, check
`agent_decisions` and `agent_tasks` for fresh rows (see ยง5 service-key note),
and `chairman-agent/data/api_costs.json` for accrued spend.
## 5. Intentionally NOT carried over (needs a human decision)
Short list now โ cost_guard, the one-tap commands, and the agent-bus writes were
ported on 2026-07-04 (table in "What moved"):
- **Mansu Musa autonomous cycle** (`agents/mansu_musa.py`, started in bot post_init)
โ the ONLY remaining functional gap. `/musa` works on demand; if the scheduled
audit cadence matters, add it to the heartbeat checklist or its own LaunchAgent.
- **/forecast, /predict commands** โ thin wrappers around
`content-os/layer_10_simulation`; the Chairman can run those modules via bash on
request. Re-add as one-tap commands only if actually missed.
### Intentional behavior changes vs org-engine (small, all stricter or safer)
- **Cutoff = hard stop, not Ollama fallback.** org-engine degraded to a local
Ollama reply when the budget tripped; the Chairman has no Ollama path, so the
bridge refuses API-calling routes (plain text, /agent, /musa runs) with a notice
+ `/unlock` instruction. The metered wrapper also stops a tool loop mid-run if
the cutoff trips during it (org only checked between conversations).
- **Supabase writes prefer the service key.** VERIFIED 2026-07-04: the shared anon
key returns 401 on `agent_decisions` INSERT since the RLS lockdown โ and
org-engine uses that SAME anon key, so its ledger writes were already failing
silently before any cutover (the dashboards' staleness predates this work).
`agent_bus.py` prefers `SUPABASE_SERVICE_KEY` (already loaded from
content-os/config/.env). **VERIFY AT CUTOVER (human eyes):** after the first
routed message, confirm a fresh row lands in `agent_decisions`, and in
`agent_tasks` after one `/agent` run. If the service key is also blocked, the
fix is a Supabase policy decision, not a bridge change.
- **No `agent_registry` startup sync** โ org-engine re-inserted missing roster
agents at every boot; the registry already holds 34 rows, so this was dropped.
- **`/musa cycle`** replies "retired โ use /musa audit". `/musa audit` also files
an `agent_reports` row (type `audit`) so `/pulse`'s LAST AUDIT stays live.
- **/costs display** adds an OpenRouter line (org budgeted it but never showed it).
## 6. mempal.md disposition
`sovereign-org-engine/agent-memory/mempal.md` is referenced by zero Python files โ
it was a passive doc, so nothing breaks. To preserve the learning:
1. Diff it against `sovereign-wiki/memory/CHAIRMAN-MEMORY.md` โ identity context and
crown-jewel IP sections are largely duplicated there already.
2. Fold any unique facts into CHAIRMAN-MEMORY.md under a heading
`## Imported from org-engine MEMPAL (2026-07-04)`.
3. Archive the originals (copy, don't move, until org-engine is retired):
```
mkdir -p ~/sovereign-empire-sweep/sovereign-wiki/memory/archive
cp ~/sovereign-empire-sweep/sovereign-org-engine/agent-memory/mempal.md \
~/sovereign-empire-sweep/sovereign-org-engine/agent-memory/learning-log.jsonl \
~/sovereign-empire-sweep/sovereign-wiki/memory/archive/
```
## Appendix: the exit-1 crash, diagnosed
`/tmp/sovereign-org-engine.log` line 4 (2026-06-28 17:18:16): python-telegram-bot's
bootstrap failed with `httpx.ConnectError: [Errno 8] nodename nor servname provided,
or not known` (DNS/network unavailable at boot) โ `Network Retry Loop (Bootstrap
Initialize Application): Failed run number 0 of 0. Aborting.` โ process exit 1 โ
launchd KeepAlive relaunch every 30s until the network returned. The same NetworkError
also spammed the log at runtime ("No error handlers are registered", thousands of
tracebacks, 7.2 MB log). The bridge's poll loop retries with capped backoff instead
of exiting, so this failure class disappears at cutover.
|