EMAIL-WP-0005-T01: add Maildir mailbox source
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Adds MaildirMailboxSource reading new/ and cur/, wired through
source.maildir_dir and mailbox.protocol: maildir. Message identity is the
Maildir unique name without its :2,FLAGS suffix, so it survives the new/ to
cur/ move; it is carried on MailboxSourceMessage.dedup_uid and appended to the
message dedup key only when set, leaving fixture and IMAP keys unchanged.

Cursor ordering parses the Maildir delivery time instead of comparing names
lexically. mark_seen and a missing directory are rejected, matching the
read-only IMAP contract.

Also fixes the parse-failure path, which keyed identity on raw_message_ref.
A new/ to cur/ move rewrites that ref, so an unparseable message re-registered
as new on every rescan; it now prefers the source uid when one exists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-08-14 01:31:58 +02:00
parent 9c0d6f1b40
commit 86f22c2e65
9 changed files with 365 additions and 18 deletions

View file

@ -9,6 +9,7 @@ owner: claude
topic_slug: custodian
created: "2026-08-14"
updated: "2026-08-14"
state_hub_workstream_id: "1ffd54d3-4fdf-4a66-86ba-36a5d8202f3c"
---
# EMAIL-WP-0005 - Test Mailbox Harness for Automated Test Environments
@ -98,8 +99,9 @@ assert stronger claims are defects in the test, not features of the harness.
```task
id: EMAIL-WP-0005-T01
status: todo
status: done
priority: high
state_hub_task_id: "28856bdc-7ea2-44ce-8c06-88bf006e17fd"
```
Tasks:
@ -123,12 +125,34 @@ same evidence rows as the equivalent fixture directory, with deduplication and
incremental cursors working across repeated scans.
```
Done 2026-08-14:
* `MaildirMailboxSource` reads `new/` and `cur/`, skips dotfiles, prefers `cur/`
when a base name appears in both, and emits
`maildir://<root>/<subdir>/<file>` refs. `mark_seen` and a missing directory
both raise, matching the read-only IMAP contract.
* Identity is the Maildir unique name minus the `:2,FLAGS` suffix, so it is
stable across the `new/``cur/` move. It is carried on a new
`MailboxSourceMessage.dedup_uid` and appended to the message dedup key only
when set, leaving existing fixture and IMAP state-store keys byte-identical.
* Cursor ordering parses the Maildir delivery time rather than comparing names
lexically, so cursors stay correct as the time field changes width.
* Fixed alongside: the parse-failure path keyed identity on `raw_message_ref`,
which a `new/``cur/` move rewrites, so an unparseable message re-registered
as new on every rescan. It now prefers the source uid when one exists.
Behavior for fixture and IMAP sources is unchanged.
* `source.maildir_dir` added to config and the example file; `source_for_config`
validates it. `include_seen: false` skips `S`-flagged messages.
* Tests: `tests/test_maildir.py`, 11 cases including maildir-vs-fixture evidence
parity. Full suite 38 passed.
## T02 - Containerized SMTP/IMAP test server
```task
id: EMAIL-WP-0005-T02
status: todo
priority: high
state_hub_task_id: "967d2bab-43e3-4240-8bd5-c657ba4178ff"
```
Tasks:
@ -165,6 +189,7 @@ does not exercise the IMAP source. Record the choice in DECISIONS.md.
id: EMAIL-WP-0005-T03
status: todo
priority: high
state_hub_task_id: "c761d26d-a8b0-4b66-ba18-8f22c6d59440"
```
Tasks:
@ -199,6 +224,7 @@ provider material; the harness must not touch them.
id: EMAIL-WP-0005-T04
status: todo
priority: high
state_hub_task_id: "69655fad-97b8-4c17-bc54-af341aaaf38c"
```
Tasks:
@ -226,6 +252,7 @@ while the default test run remains offline, deterministic, and unchanged.
id: EMAIL-WP-0005-T05
status: todo
priority: medium
state_hub_task_id: "48b8eaf9-5a4b-437b-ad2c-952907340ed7"
```
Tasks:
@ -252,6 +279,7 @@ papered over.
id: EMAIL-WP-0005-T06
status: todo
priority: medium
state_hub_task_id: "0599de98-c0ff-4160-a2c8-54265b31ca5d"
```
Tasks: