Audit: envoy/todo mail server item

Every assumption in the top todo item was checked against the live system (2026-08-09). Most are wrong or obsolete.

Claim: CNAME mail.critchley.biz → gravlax not yet added

WRONG. The CNAME is in place and resolving.

Evidence: host mail.critchley.biz returns: "mail.critchley.biz is an alias for gravlax.critchley.biz. gravlax.critchley.biz has address 16.16.198.94"

Claim: gravlax has no Dovecot installed (clean Debian 12)

WRONG. Dovecot is installed, running, and has a valid Let's Encrypt cert for mail.critchley.biz.

Evidence: openssl s_client -connect mail.critchley.biz:993 completes TLS handshake: depth=0 CN=mail.critchley.biz, verify return:1. IMAP greeting: "Dovecot (Debian) ready."

Claim: Ansible playbook not yet run

WRONG (implied by above). Dovecot is fully configured — IMAPS port 993, valid cert, virtual users, INBOX accessible. Playbook has been run.

Claim: popit3 delivers to local Maildir on pomelo only

WRONG. popit3 already uses imap_deliver.py which delivers via IMAP APPEND directly to mail.critchley.biz:993.

Evidence: /home/john/py/popit3/imap_deliver.py line 16: IMAP_HOST = 'mail.critchley.biz', port 993. process_emails.py routes envoy@critchley.biz to imap_deliver.deliver_to_envoy.

Claim: Envoy's stunnel IMAP path points at cv.critchley.biz:993

WRONG / MOOT. The [imap] stunnel section is commented out in /etc/stunnel/stunnel.conf. Envoy connects directly (no stunnel) to mail.critchley.biz:993 via imaplib.IMAP4_SSL().

Evidence: orchestrator.py: IMAP_HOST = 'mail.critchley.biz'. stunnel.conf [imap] section is commented out; active sections are notes (8021), notes-mcp (8023), postgres (5432), socks5h (1080).

Claim: Envoy not replying — root cause is mail server migration

WRONG. The mail pipeline is fully functional end-to-end. The missing-reply problem has a different cause.

Evidence: Envoy cron runs every 5 minutes (5am–10pm). Recent log (2026-08-09 09:57): IMAP connects to mail.critchley.biz, finds 17 messages in INBOX, reports "Inbox drain complete: no UNSEEN emails remaining." All 17 are already SEEN.

Actual Symptoms / Real Issues to Investigate

1. Phase registry warning — every run since at least 2026-07-28: "Could not load phase registry from notes ('str' object has no attribute 'get') — using hardcoded defaults". This is a live bug in orchestrator.py; the note at envoy/states/index (or wherever the registry lives) may be malformed or the fetch is returning a string instead of a dict.

2. 17 SEEN messages in INBOX — unclear if Envoy processed the test emails (2026-07-19, 2026-07-27, 2026-07-31) and failed to reply, or if they arrived already seen. Need to check: (a) are those messages present, (b) what Envoy did when it processed them, (c) whether send_emails ran and if SMTP succeeded.

3. No evidence of outgoing mail — no send_email or SMTP log entries visible in the grep of envoy.log. Either Envoy isn't reaching the send phase, or SMTP is failing silently.

Recommended Todo Update

Mark the mail server migration item ✅ Complete (all infrastructure is in place). Replace it with two new items: (1) fix the phase registry bug, (2) diagnose why no SMTP output is seen when Envoy processes emails.

Fix: Phase Registry Bug

Root cause (two problems): 1. The note envoy/states/index was lost (not in live store, though present in Apr 2026 backup). 2. notes_client.read_doc() returns a plain string on 404 ("Document ... not found") rather than raising an exception. orchestrator.py calls doc.get() on that string, causing AttributeError which the except clause logs as the warning seen in every run since 2026-07-28.

Fix A — Restore envoy/states/index: PUT the note back from backup content. non_terminal_phases: triage, gathering, summarising, working, coding, composing, waiting, inbox_scan.

Fix B — Defensive check in load_phase_registry(): after read_doc(), check isinstance(doc, dict) before calling doc.get(). Log a clear warning and fall back if doc is a string. This makes the function robust against future notes_client API surprises.

Investigation: Why Envoy Was Not Replying

Conclusion (2026-08-09): Envoy IS working correctly. A reply was confirmed sent to johnsrcritchley@gmail.com on 2026-08-05 11:17 re ‘Work through open items: ansible/todo’ via Brevo SMTP. Processing complete, stored in Sent.

Root cause of the ‘no reply’ period (2026-07-20 to 2026-08-05): The gdata notes server (gravlax:18021 via stunnel) was intermittently down, producing ‘Connection reset by peer’ and HTTP 500 errors. This caused Fatal error in main before any LLM or SMTP call could be made. 16 emails from that period were processed far enough to receive \Seen + $EnvoyAnalysed flags (during inbox_scan header fetch and body fetch) but not far enough to send replies. They are now permanently excluded from Envoy’s search (which requires UNSEEN + UNKEYWORD $EnvoyAnalysed).

Fix applied (2026-08-09): Restored envoy/states/inbox_scan from Apr 2026 backup (was missing, causing a WARNING on every run since before Aug 5). No code change needed — the stuck emails are old test emails (Feb–Jul 2026) and can be left or deleted. The notes server is currently stable.

version 3  ·  created 2026-08-09  ·  updated 2026-08-09