Changelog — 2026-02-22

Phase-Aware Processing State Machine (Major Feature)

See Design Note and Code Overview for full details.

9 phases: triage, gathering, summarising, working, coding, composing, waiting (non-terminal) + complete, escalate (terminal).• dispatch_immediate_actions() runs write_notes, delete_notes, send_emails on every non-terminal phase• Per-state instruction notes: envoy/states/{phase} auto-loaded before each LLM call• Context bundles: LLM sets bundle_key; orchestrator auto-loads listed notes each iteration• Scratch notes pattern: temporary notes deleted at task completion• Model tier defaults per state: coding → full, triage/summarising → mini

Bug Fixes

Double-send on complete: When LLM replied in composing phase, execute_actions sent a duplicate confirmation. Fixed by tracking sent_to_addresses set through dispatch_immediate_actions and checking prior_sent_to in execute_actions.• Duplicate notes in context: LLM requesting already-loaded notes caused them to be appended again. Fixed with already_loaded_keys dedup check.• Move-email INBOX-only search: execute_actions only searched INBOX; on continuation resume the email might be in Active. Fixed by scanning INBOX then Active.• IMAP APPEND marks as Seen: All APPEND'd messages were immediately Seen. Fixed by explicitly calling store('-FLAGS', '\\Seen') after APPEND — encapsulated in test_orchestrator.py.

New Tooling

• test_orchestrator.py: Test harness with --inject, --resume, --mark-unseen, --list, --run, --max-iter flags• Continuation email Sent IMAP copy: stored immediately after sending; enables --resume workflow without waiting for external mail loop

Files Changed

• orchestrator.py — Phase-aware FSM, dispatch_immediate_actions(), bundle_key/current_phase, double-send fix, dedup fix, multi-folder move search, Sent IMAP copy in send_continuation_email• envoy_schema.py — Added bundle_key, current_phase, delete_notes fields• new_envoy_response_schema.json — Matching schema updates• test_orchestrator.py — New test harness (new file)

Testing

Full end-to-end test with 7 iterations across a continuation boundary:• Intermediate email sent during composing phase• Scratch notes written during gathering, deleted at complete• Original email moved to Done• No duplicate sends, no duplicate notes in context• Continuation correctly preserved bundle_key, current_phase, failed_fetches

Orchestrator Improvements (same session, later)

• Email length guard: EMAIL_BODY_WARN_CHARS = 4000 constant. dispatch_immediate_actions() logs a WARNING before sending any email body exceeding this length. Trains the LLM to use attachments instead of inlining large content.

• Approach tracking: attempted_searches: list[str] accumulates a description of every search_emails_imap() call made in the current run. Persisted in continuation.json alongside failed_fetches. Surfaced to the LLM as === SEARCHES TRIED THIS RUN === to break repetition loops.

• Note-write repair: _parse_note_value(key, value) added to orchestrator.py. Tries json.loads(), then applies regex repair for the \\" encoding bug (see note-encoding issue), then raises ValueError — never silently falls back to old-style text notes.

• notes_client.py: removed silent old-style-text fallback from write_doc(). Invalid JSON now raises ValueError immediately.

Notes System Fixes

• CONTENTS stale link: deleting/renaming notes without updating CONTENTS caused Envoy to attempt dead fetches, mark them permanently unavailable, and proceed without the right context. Root cause of the 2026-02-22 JSONHTL converter run failure. Fix: always update CONTENTS when renaming or deleting a note.

• Dead link recovery procedure documented in README/links: Approach 1 (navigate CONTENTS hierarchy), Approach 2 (full key scan). On finding a note by either route, fix the dead href and link the note from its proper parent.

• Key typo fix: projects/JSOHTL-HTML-converter (missing N) → projects/JSONHTL-HTML-converter. Three sub-notes migrated; CONTENTS and bundle updated.

Instruction Note Updates

• envoy/start: rewrote Writing Notes section; removed old-style note instructions; added codeblock encoding rules (prefer single-quoted Python, \" not \\").

• envoy/states/coding v2: added 'Where to store code' (whole file not per-routine fragmentation) and 'Codeblock encoding — critical' sections.

• envoy/states/triage, gathering, composing: updated with self-retrieval rule, notes-first gathering order, vary-approach-after-failure rule, attachment I/O guidance, status footer for non-final emails, code-by-reference rule.

• README/links: added 'Dead Link Recovery' section with two-approach procedure.

version2
date2026-02-22