diff --git a/machine-docs/BACKLOG-mailu.md b/machine-docs/BACKLOG-mailu.md index c2b0111..3d34433 100644 --- a/machine-docs/BACKLOG-mailu.md +++ b/machine-docs/BACKLOG-mailu.md @@ -8,7 +8,7 @@ ### [ADV-mailu-01] `/mail` Maildir volume restoration not tested — seed too shallow [adversary] **Filed**: 2026-06-11T20:58Z -**Status**: OPEN — blocks M1 +**Status**: FIXED (build #477) — pending Adversary close **Plan requirement** (`plan-phase-mailu-backup.md` §2.3): "a seeded mailbox + message that survives backup→wipe→restore — extend the existing functional helpers if the current seed is too shallow" diff --git a/machine-docs/JOURNAL-mailu.md b/machine-docs/JOURNAL-mailu.md index c19aa0a..9f63d14 100644 --- a/machine-docs/JOURNAL-mailu.md +++ b/machine-docs/JOURNAL-mailu.md @@ -4,6 +4,24 @@ Design rationale, dead-ends, investigation notes. Not for Adversary pre-verdict --- +## 2026-06-11 ADV-mailu-01 fix — build #477 LEVEL 5 re-verified + +### ADV-mailu-01 resolution confirmed + +Build #477 result confirms both volumes are now specifically tested: +- `test_backup_captures_mail_message` PASS: `ccci-backup-probe` message in INBOX at backup time +- `test_restore_returns_mail_message` PASS: message survives Maildir wipe + restore from snapshot +- Both maildir-specific tests ran in the `backup` and `restore` stages respectively +- Full build level 5, clean_teardown=true, no_secret_leak=true + +The `sendmail` delivery path (smtp container → postfix → dovecot deliver) worked correctly +for injecting the test message. The `doveadm search` poll with 60s timeout was sufficient. +The `rm -rf /mail//citest` wipe in pre_restore fully cleared the Maildir before restore. + +Re-claiming M1 with build #477 as the evidence build. + +--- + ## 2026-06-11 Bootstrap + data-layout research ### mailu volume layout (from compose.yml analysis) diff --git a/machine-docs/STATUS-mailu.md b/machine-docs/STATUS-mailu.md index 9b479fe..8caf531 100644 --- a/machine-docs/STATUS-mailu.md +++ b/machine-docs/STATUS-mailu.md @@ -8,10 +8,15 @@ ## Current state -**Gate M1: CLAIMED — awaiting Adversary** +**Gate M1: CLAIMED (re-claim after ADV-mailu-01 fix) — awaiting Adversary** -Drone build #473: LEVEL 5 PASS at PR#3 head (edc0201a79d3), all rungs green including -backup/restore on real seeded mail data. Claimed 2026-06-11T20:52Z. +Drone build #477: LEVEL 5 PASS at PR#3 head (edc0201a79d3), all rungs green including +backup/restore on real seeded mail data — BOTH `/data` (SQLite) AND `/mail` (Maildir) now +specifically tested and verified. Claimed 2026-06-11T21:XX. + +ADV-mailu-01 fix: extended `ops.py::pre_backup` to inject a real mail message; extended +`ops.py::pre_restore` to also wipe the Maildir; added `test_backup_captures_mail_message` and +`test_restore_returns_mail_message` to verify both volumes survive the cycle. **Gate M2:** NOT YET CLAIMED @@ -27,16 +32,19 @@ backup/restore on real seeded mail data. Claimed 2026-06-11T20:52Z. - Adds `deploy.labels: {backupbot.backup: "true", backupbot.backup.path: "/data"}` to `admin` - Adds `deploy.labels: {backupbot.backup: "true", backupbot.backup.path: "/mail"}` to `imap` - [x] Version label bumped in compose.yml (3.0.1 → 3.0.2+2024.06.52) -- [x] cc-ci: `tests/mailu/ops.py` with pre_backup (seed mailbox) + pre_restore (delete mailbox) -- [x] cc-ci: `tests/mailu/test_backup.py` asserting mailbox present at backup time -- [x] cc-ci: `tests/mailu/test_restore.py` asserting mailbox restored after restore +- [x] cc-ci: `tests/mailu/ops.py` with pre_backup (seed mailbox + inject mail message) + pre_restore (delete mailbox + wipe Maildir) +- [x] cc-ci: `tests/mailu/test_backup.py` asserting mailbox AND mail message present at backup time +- [x] cc-ci: `tests/mailu/test_restore.py` asserting mailbox AND mail message restored after restore - [x] cc-ci: `tests/mailu/PARITY.md` updated (P4 now covered, not N/A) -- [x] Full lifecycle green at PR head (L5) including backup/restore rung — via drone `!testme` - - **Drone build #473**: LEVEL 5 of 5 — all rungs PASS (install/upgrade/backup/restore/custom) +- [x] Full lifecycle green at PR head (L5) including backup/restore on BOTH volumes — via drone `!testme` + - **Drone build #477**: LEVEL 5 of 5 — all rungs PASS (install/upgrade/backup/restore/custom/lint) - `test_backup_captures_mailbox` PASS — `citest@` present in config-export at backup time + - `test_backup_captures_mail_message` PASS — message `ccci-backup-probe` in INBOX at backup time - `test_restore_returns_mailbox` PASS — `citest@` restored after pre_restore deletion - - Backup snapshot: `13eee64e` (139 files, 88MB, admin `/data` + imap `/mail`) - - Clean teardown: no `mailu-*` stack left on host (`docker stack ls` confirms) + - `test_restore_returns_mail_message` PASS — `ccci-backup-probe` message back after Maildir wipe+restore + - `clean_teardown: true`, `no_secret_leak: true` + - No mailu stacks on host post-run (`docker stack ls` confirms) +- [x] ADV-mailu-01 fix applied: seed now covers both volumes (`/data` SQLite + `/mail` Maildir) - [x] Before/after level recorded - **BEFORE** (main, no labels): `backup_capable=False` → backup rung = intentional-skip → max **L4** - **AFTER** (PR#3 head edc0201a): `backup_capable=True` (auto-detected from labels) → backup rung = PASS → **L5** @@ -51,7 +59,7 @@ backup/restore on real seeded mail data. Claimed 2026-06-11T20:52Z. --- -## Verification recipe (for Adversary M1 check) +## Verification recipe (for Adversary M1 re-check) ```bash # 1. Verify backupbot v2 labels in PR#3 compose.yml (branch: add-backupbot-labels) @@ -67,9 +75,9 @@ curl -s "https://git.autonomic.zone/api/v1/repos/recipe-maintainers/mailu/conten # 2. Verify PR#3 head commit # Expected: edc0201a79d36bc87696b0f93f1ee88ad7bd10ed -# 3. Verify drone build #473 level 5 +# 3. Verify drone build #477 level 5 DRONE_TOKEN=$(ssh cc-ci 'cat /run/secrets/bridge_drone_token') -curl -s "https://drone.ci.commoninternet.net/api/repos/recipe-maintainers/cc-ci/builds/473" \ +curl -s "https://drone.ci.commoninternet.net/api/repos/recipe-maintainers/cc-ci/builds/477" \ -H "Authorization: Bearer ${DRONE_TOKEN}" | python3 -c " import sys,json; b=json.load(sys.stdin) print('status:', b['status']) @@ -77,14 +85,24 @@ print('steps:', [(s['name'], s['status']) for st in b['stages'] for s in st['ste " # Expected: success, clone+ci both success -# 4. Verify full results.json -ssh cc-ci 'cat /var/lib/cc-ci-runs/473/results.json' -# Expected: level=5, all rungs pass (backup_restore, functional, install, lint, upgrade) +# 4. Verify full results.json — including NEW maildir tests +ssh cc-ci 'cat /var/lib/cc-ci-runs/477/results.json' +# Expected: +# level=5, all rungs pass (backup_restore, functional, install, lint, upgrade) +# backup stage tests include: +# test_backup_captures_mailbox PASS (SQLite /data) +# test_backup_captures_mail_message PASS (Maildir /mail) +# restore stage tests include: +# test_restore_returns_mailbox PASS (SQLite /data) +# test_restore_returns_mail_message PASS (Maildir /mail) +# flags: clean_teardown=true, no_secret_leak=true -# 5. Re-trigger to verify at current PR head (M2 requirement): +# 5. Verify clean teardown on host +ssh cc-ci 'docker stack ls | grep -i mailu || echo "no mailu stacks (clean)"' +# Expected: no mailu stacks + +# 6. Re-trigger to verify at current PR head (M2 requirement): # Comment !testme on PR#3 as the Adversary and observe level 5 again - -# 6. Confirm DEFERRED entry for mailu backup is closed (see machine-docs/DEFERRED.md) ``` ---