Auto-Reply Debug Session 2026-07-21
Timeline
- Watcher daemon died after phone restart (or process killed), ~7/19 15:38
- Orchestrator stopped processing incoming.jsonl at the same time
- 5579 messages accumulated in incoming.jsonl
- Alisa (wxid_qx3jhn1edwie22, role=admin) sent msgId 18334 at 7/21 09:48: 「不错啊,和 BOSS 直聘打通了呀」
- Message was captured by watcher --once but orchestrator couldn't process it
Root Causes (layered)
Layer 1: Watcher needs su -c
MM_BASE = /data/user/0/com.tencent.mm — this path is inaccessible without root.
read_uin() uses open(xml) directly, not su -c cat, so it fails with
"没读到微信 last_login_uin(未登录?没 root?)" when run as normal user.
Fix: su -c '/data/data/com.termux/files/usr/bin/python3 /data/data/.../watcher.py'
Layer 2: Orchestrator also needs su -c
_drain_inner() → account_paths() → read_uin() — same root requirement.
When run as normal user, exception is thrown silently (stdout not captured in cron context),
cursor is never updated, orchestrator log shows no new entries.
Fix: su -c '/data/data/com.termux/files/usr/bin/python3 /data/data/.../orchestrator.py --drain'
Layer 3: orch.lock residue
When orchestrator crashes under root, drain() finally block can't delete orch.lock
(root-owned file, or exception jumped past finally). Subsequent drains see the lock and
return immediately (no log). Lock is auto-preempted after 300s, but in those 5 minutes
all drains are silent no-ops.
Fix: Watchdog now cleans locks older than 300s before each drain.
Layer 4: copy_db misses rcontact table
Watcher's copy_db() creates an EnMicroMsg.db copy under _stage_orch/ but doesn't
include rcontact table. When process_event() tries to look up sender nickname,
sqlcipher reports no such table: rcontact. The message send itself may still succeed
via hook, but processing is logged as failed.
Status: known bug, not yet fixed.
Recovery Steps (executed)
su -c '...watcher.py --once'— caught up missed messages (msgId to 18387)- Set cursor.json to
{"last_msgId": 18387} su -c '...watcher.py &'— started persistent daemonrm -f orch.lock— cleared residue locksu -c '...orchestrator.py --drain'— processed backlog- Manually sent reply to Alisa via send_text_via_hook (orchestrator's rcontact bug blocked automatic processing of #18334)
Prevention
Watchdog script (~/pixelphone/scripts/wechat-viewer-watchdog.sh) now includes:
- §5: WeChat Watcher daemon check + auto-restart (with su -c)
- §6: Orchestrator drain every 5 minutes (with su -c + lock cleanup)