Plan: Apple Notes over IMAP (#543) #766
Open
kayg
wants to merge 1 commit from
docs/imap-notes-plan into dev
pull from: docs/imap-notes-plan
merge into: kayg:dev
kayg:main
kayg:feat/contrast
kayg:feat/style-sheet-fixes
kayg:storage/wal-checkpoint-off-save-1051
kayg:t3/paged-search-reconcile-final
kayg:feat/pass8
kayg:review/calendar-2
kayg:fix/shared-row-swipes
kayg:dev
kayg:feat/collections-db
kayg:fix/instant-keys-1056
kayg:feat/money-round
kayg:perf/stalls-1054
kayg:wip/tiering-v3-cold4
kayg:wip/tiering-v3-cold5
kayg:port/rows-actions
kayg:port/rows-drop
kayg:port/hidden-blocks
kayg:port/rows
kayg:port/port-tables
kayg:port/port-tables-2
kayg:t3/mail-1011-liveness
kayg:port/devices
kayg:port/port-attachments
kayg:port/port-attachments-2
kayg:port/port-attachments-3
kayg:docs/design-consistency
kayg:docs/engineering-standards
kayg:fix/rusqlite-0.41-rustsec-2026-0459
kayg:feat/iphone-fixes
kayg:shared/restore-and-stale-918-919
kayg:fix/phone-top-band-contrast
kayg:feat/notes-approve
kayg:perf/soak-max-gate
kayg:feat/files-s1-identity
kayg:fix/lifecycle-event-id
kayg:feat/quickfix
kayg:wip/tiering-v3-cold
kayg:perf/web-mockup-frame-fixes
kayg:port/contacts-merge
kayg:port/contacts-edit
kayg:test/shared-room-no-deleted-text
kayg:perf/first-paint-credits
kayg:fix/shared-history-after-grant
kayg:fix/phone-swipe-regressions
kayg:feat/contrast-r1
kayg:review/sharing-pr3-review
kayg:feat/tiering-v3-s2-tags
kayg:feat/folder-drop
kayg:fix/deskpills-lazy-share
kayg:feat/sharing-s1-pr3
kayg:tmp/money-round-before
kayg:fix/phone-pane-backdrop-roots
kayg:fix/palette-preload-chips
kayg:fix/invite-defaults
kayg:port/tasks-matrix
kayg:fix/restore-green-gates
kayg:fix/sharing-fast-path-hardening
kayg:t3/photos-s1
kayg:feat/tiering-v3-s1-namespace
kayg:notes-archive-hide
kayg:feat/tasks-actions
kayg:t3/photos-plan-slice-one
kayg:feat/whiteboard-s1
kayg:t3/paged-search-reconcile
kayg:t3/smooth-pane-transitions
kayg:t3/index-position-startup-scan
kayg:fix/private-staging-759b-r2
kayg:feat/dav-transport
kayg:feat/imap-notes-fetch
kayg:feat/imap-notes-projection
kayg:feat/imap-notes-readonly
kayg:feat/imap-notes-auth
kayg:feat/imap-notes-server-foundation
kayg:docs/undo-block-ids-plan
kayg:fix/undo-768
kayg:proto/block-identity-v7d
kayg:issue/carddav-read-perf-fix-r2
kayg:issue/carddav-read-r2
kayg:docs/imap-766-plan-v2
kayg:docs/quota-plan-2
kayg:fix/parallel-test-flakes
kayg:feat/contacts-companion-pure
kayg:docs/calendar-backend-v2-plan
kayg:feat/calendar-ical-converter
kayg:docs/tasks-writes-v2-plan
kayg:docs/carddav-writes-v2-plan
kayg:docs/contacts-import-v2-plan
kayg:issue/610-pm-midnight
kayg:feat/calendar-v2-s1-containment
kayg:docs/dav-recorder-v2-plan
kayg:plan/previews-865
kayg:plan/save-log-durable-entry
kayg:fix/e2e-data-on-ram
kayg:feat/appwindow3
kayg:feat/appwindow2
kayg:feat/appwindow
kayg:feat/money-p1
kayg:feat/keys-sheet
kayg:review/calendar-2-themes
kayg:feat/smooth-cursor
kayg:fix/float-style
kayg:review/calendar-2-float
kayg:release/2026-11-06-hotfix
kayg:archive/port/rows-drop-code
kayg:archive/port/rows-actions
kayg:archive/port/rows
kayg:scratch/tasks-894-ui-gates
kayg:review/calendar-2-night-wip
kayg:release/2026-11-05
kayg:archive/feat/tiering-v3-s1-namespace
kayg:archive/flaky/rows-drop-fixed
kayg:archive/flaky/rows-actions
kayg:archive/feat/tiering-v3-reader
kayg:archive/feat/tiering-v3-roots
kayg:archive/flaky/rows
kayg:sim/bm15-final
kayg:sim/bm15-fix
kayg:sim/bm15-bis-932
kayg:sim/bm15-bis-909
kayg:sim/bm15
kayg:sim/bm15-bis-887
kayg:perf/tiering-option-b
kayg:port/style-3
kayg:port/style-2
kayg:port/style
kayg:fix/sw-lazy-precache
kayg:fix/vim-w-waits-for-save
kayg:fix/signout-test-races
kayg:fp-scratch/all
kayg:sim/bm15-first-paint
kayg:archive/bm15/rows-drop
kayg:archive/bm15/rows-actions
kayg:fix/palette-snippet-contrast
kayg:archive/bm15/rows
kayg:archive/bm15/hidden
kayg:archive/plan/save-log-durable-entry
kayg:mock/money-fix2
kayg:archive/fix/e2e-data-on-ram
kayg:mock/appearance-all
kayg:mock/pill-press
kayg:archive/docs/design-consistency
kayg:mock/auto-swipes
kayg:mock/mail-actions
kayg:archive/feat/mail-p1
kayg:mock/money-static
kayg:archive/review/standards-917
kayg:archive/docs/engineering-standards
kayg:perf/tiering-761-fix
kayg:mock/type-audit
kayg:mock/third-pane
kayg:feat/contacts-import-v2-s1
kayg:perf/executor-hot-path
kayg:archive/port/hidden-blocks
kayg:archive/port/devices
kayg:scratch/executor-on-894
kayg:scratch/executor-spans2
kayg:scratch/executor-spans
kayg:train/2026-11-03-bm14c
kayg:archive/port/rows-drop
kayg:feat/tiering-761-d-scale
kayg:feat/tiering-761-c-index
kayg:feat/tiering-761-b-reader
kayg:archive/review/sharing-pr3-review-9fec3d1f
kayg:train/2026-11-03-bm14b
kayg:perf/ports-lazy-budgets
kayg:feat/tiering-761-a-migration
kayg:train/2026-11-03-bm14
kayg:train/2026-11-03-bm12
kayg:archive/review/sharing-pr3-review
kayg:archive/feat/sharing-s1-pr3
kayg:train/2026-11-03-bm11
kayg:archive/feat/imap-notes-fetch
kayg:train/2026-11-03-bm10
kayg:archive/review/sharing-pr3-review-code
kayg:archive/plan/previews-865
kayg:train/2026-11-02-bm9
kayg:archive/port/port-attachments-3
kayg:archive/port/contacts-merge
kayg:fix/motion-audit-859
kayg:train/2026-11-02-bm8
kayg:archive/scratch/bm8-prep
kayg:archive/port/port-pickers
kayg:archive/port/port-tables-2
kayg:archive/port/port-tables
kayg:archive/feat/sharing-s1-pr3-code
kayg:archive/scratch/bm7-fixed
kayg:train/2026-11-02-bm7
kayg:archive/fix/parallel-test-flakes-code-minipc01
kayg:train/2026-11-02-bm6
kayg:docs/imap-766-readonly-slice
kayg:feat/dav-recorder-v3
kayg:research/788-diagnosis
kayg:archive/fix/parallel-test-flakes
kayg:archive/perf/pr792-cold-index-edit
kayg:docs/sharing-slice-1
kayg:archive/docs/imap-766-plan-v2
kayg:feat/dav-recorder-v2
kayg:archive/docs/dav-recorder-v2-plan
kayg:spike/imap-766-s0-cases
kayg:archive/fix/undo-768
kayg:feat/dav-recorder
kayg:archive/docs/contacts-import-v2-plan
kayg:archive/feat/calendar-ical-converter
kayg:feat/contacts-import
kayg:perf/cold-growth-on-764
kayg:perf/cold-growth-on-tiering
kayg:archive/docs/tasks-writes-v2-plan
kayg:archive/docs/calendar-backend-v2-plan
kayg:archive/feat/contacts-companion-pure
kayg:feat/tasks-set-status
kayg:feat/tasks-move-reorder
kayg:archive/docs/carddav-writes-v2-plan
kayg:archive/feat/calendar-v2-s1-containment
kayg:feat/tasks-delete-promote-duplicate
kayg:archive/feat/dav-transport
kayg:archive/fix/carddav-rebuild-r1
kayg:archive/fix/carddav-retention-r1
kayg:feat/carddav-writes
kayg:perf/quota-on-tiering
kayg:perf/quota-simple
kayg:perf/cold-growth-on-quota-stack
kayg:feat/calendar-identity
kayg:feat/calendar-event-parser
kayg:feat/calendar-props-maps
kayg:port/glass-when-ring
kayg:feat/tiering-761-s1
kayg:wip/10by32-01-impl-759b-scratch-20261030
kayg:wip/6by32-01-bm3-webkit-pause-20261030
kayg:wip/10by32-01-bm3-chromium-pause-20261030
kayg:wip/6by32-01-tier-761-s1-pause-20261030
kayg:spike/imap-766-s0
kayg:wip/6by32-02-calternal-pause-20261030
kayg:wip/8by64-01-review-688-pause-20261030
kayg:wip/6by32-01-verify-744-pause-20261030
kayg:wip/6by32-01-verify-742-pause-20261030
kayg:fix/private-staging-759b
kayg:wip/6by32-01-review-748-pause-20261030
kayg:wip/6by32-01-review-733c-pause-20261030
kayg:wip/6by32-01-review-733b-pause-20261030
kayg:wip/6by32-01-perf-idle-8-pause-20261030
kayg:wip/6by32-01-perf767-base-pause-20261030
kayg:wip/6by32-01-perf767b-767-pause-20261030
kayg:wip/6by32-01-perf767b-764-pause-20261030
kayg:wip/6by32-01-perf767-767-pause-20261030
kayg:wip/6by32-01-perf767-764-pause-20261030
kayg:wip/6by32-01-lists-b-pause-20261030
kayg:wip/6by32-01-ids-verify5-pause-20261030
kayg:wip/6by32-01-ids-verify4-pause-20261030
kayg:review/calendar-month
kayg:wip/10by32-02-undo-768-20261030
kayg:test/ids-verify3
kayg:wip/10by32-02-bm3-chromium-20261030
kayg:wip/10by32-01-review-753-20261030
kayg:wip/10by32-01-perf-idle-2-20261030
kayg:wip/10by32-01-bm2-webkit2-20261030
kayg:plan/759b-staging
kayg:train/2026-10-30-bm2
kayg:review/758b-tests
kayg:review/758-tests
kayg:review/764d-tests
kayg:review/767b-tests
kayg:review/preview-2026-10-29
kayg:review/767-tests
kayg:docs/tiering-731-plan
kayg:wip/c6b02-when2
kayg:wip/c6b02-tasks-create-fix5
kayg:wip/c6b02-settings-frost-mock
kayg:wip/c6b02-settings-frost-base
kayg:wip/c6b02-settings-frost
kayg:wip/c6b02-ring
kayg:wip/c6b02-review-733d
kayg:wip/c6b02-bm1-full-a
kayg:wip/compute02-when-field
kayg:wip/compute02-webkit-lists-repro
kayg:wip/compute02-train-bl2-dev
kayg:wip/compute02-dev-green-pw164
kayg:docs/deploy-plan
kayg:review/764c-tests
kayg:review/764b-tests
kayg:review/748g-tests
kayg:review/764-tests
kayg:review/759b-tests
kayg:fix/storage-owner-liveness-642
kayg:fix/storage-fix-7
kayg:wip/compute02-storage-fix-7
kayg:review/759-tests
kayg:feat/tiering-731
kayg:wip/tiering-731-first-attempt
kayg:wip/compute02-storage-fix-1
kayg:review/754g-tests
kayg:perf/idle-honest-2
kayg:review/754f-tests
kayg:review/754e-tests
kayg:review/755-tests
kayg:review/754d-tests
kayg:review/754c-tests
kayg:review/754b-tests
kayg:review/754-tests
kayg:fix/storage-private-tmp-642-dev
kayg:wip/compute02-verify-750
kayg:test/ids-verify5
kayg:test/ids-verify4
kayg:fix/storage-exact-file-quarantine-642
kayg:wip/compute02-verify-740
kayg:perf/one-flush-save
kayg:review/calendar-static
kayg:wip/compute02-verify-737
kayg:review/indexing-ring
kayg:review/when-field-2
kayg:wip/compute02-review-733
kayg:proto/block-identity-2-review
kayg:proto/block-identity-review
kayg:wip/c6b02-tc-design
kayg:review/when-field
kayg:issue/tasks-create-ids
kayg:issue/tasks-create
kayg:review/combined
kayg:wip/compute02-review-combined
kayg:review/task-polish
kayg:review/top-buttons
kayg:review/q1-q5
kayg:train/2026-10-26-bl2
kayg:archive/wt-398h
kayg:archive/train-bm-proptest-seed
kayg:archive/integ325-293
kayg:archive/integ325-ci
kayg:archive/client-auth-review-baseline
kayg:archive/wt-sim-4853
kayg:archive/wt-sim-4811
kayg:archive/wt-448m
kayg:archive/wt-398a-allocator
kayg:issue/calendar-adr
kayg:archive/old/train/2026-10-23bk2
kayg:archive/old/train/2026-10-23bk
kayg:issue/469-mockup-9a
kayg:issue/smart-tasks-mockup
kayg:issue/469-menus
kayg:issue/652-signout-flake
kayg:issue/652-history-fix7
kayg:issue/652-history-fix6
kayg:archive/old/issue/spring-check-perf
kayg:issue/spring-check-perf
kayg:issue/659-rename-fast
kayg:archive/old/train/2026-10-21ar
kayg:issue/search-hardening-3
kayg:issue/652-history-fix5
kayg:issue/660-port-sheets-glass
kayg:issue/652-history-fix4
kayg:issue/652-history-fix3
kayg:issue/652-history-fix2
kayg:issue/652-history-flake
kayg:train/2026-10-21an
kayg:issue/settings-admin-port
kayg:issue/search-port
kayg:train/2026-10-20am
kayg:issue/storage-split-fix2
kayg:issue/storage-split
kayg:archive/old/issue/folder-ids-fix
kayg:train/2026-10-20al
kayg:archive/old/issue/folder-ids
kayg:train/2026-10-20ak
kayg:issue/tables-mockup
kayg:train/2026-10-19aj
kayg:train/2026-10-19ai
kayg:issue/469-merge-fixes
kayg:issue/sidebar-band
kayg:archive/old/issue/608-settings-spec
kayg:train/2026-10-19ah
kayg:issue/swipe-offscreen
kayg:train/2026-10-19ag
kayg:archive/old/issue/590-nlp-adr
kayg:archive/old/issue/587-event-after-write-2
kayg:train/2026-10-19af
kayg:archive/old/issue/587-event-after-write
kayg:issue/469-pill-room-c
kayg:issue/469-pill-room
kayg:train/2026-10-19ae
kayg:issue/469-narrow-handle
kayg:issue/397-ios-swipe-2
kayg:archive/old/issue/adrs-photos-files-tasks
kayg:issue/469-desk-fixes
kayg:archive/old/issue/auto-update-adr
kayg:issue/469-filter-gap
kayg:issue/469-port-1b
kayg:issue/442-opacity-flake
kayg:archive/397-ios-swipe-2
kayg:issue/327-web-fast-3
kayg:archive/old/issue/469-port-1
kayg:issue/469-port-1
kayg:issue/327-web-fast-2
kayg:archive/old/issue/327-web-fast
kayg:archive/old/issue/425-stt-bench-vq1
kayg:archive/old/issue/445-no-bleed
kayg:archive/old/issue/400-swipe-mockup-2
kayg:issue/426-client-review
kayg:issue/426-client-2
kayg:issue/426-client
kayg:archive/old/issue/476-safe-area-lab
kayg:archive/445-top-light-2
kayg:archive/old/issue/445-top-light
kayg:issue/471-blocks-mockup
kayg:archive/old/issue/448-links-app-2
kayg:archive/old/issue/448-links-app
kayg:archive/old/issue/398-cold-links
kayg:issue/434-442-small-2
kayg:issue/434-442-small
kayg:archive/old/issue/398-unload
kayg:archive/old/issue/445-bottom-band
kayg:issue/445-bottom-band
kayg:issue/351-rem-3
kayg:archive/312-views-mockup
kayg:issue/351-rem-2
kayg:issue/351-rem
kayg:archive/old/issue/327-first-note
kayg:issue/395-width-phone
kayg:archive/old/issue/327-typing
kayg:issue/396-ios-edges
kayg:issue/397-ios-swipe
kayg:archive/398-allocator
kayg:archive/old/issue/327-notes-index
kayg:issue/345-347-contrast-empty-2
kayg:archive/old/issue/44-arenas
kayg:issue/44-arenas
kayg:archive/integ325
kayg:archive/old/issue/327-perf-harness
kayg:archive/old/issue/44-thp
kayg:issue/44-thp
kayg:archive/old/issue/285-sticky-pills
kayg:archive/old/issue/44-trim-3
kayg:issue/44-trim-3
kayg:archive/old/issue/165-cards
kayg:archive/old/issue/165-picker-2
kayg:archive/old/issue/212-multi-select
No reviewers
Labels
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set
Reference
kayg/calternal!766
Loading…
Reference in a new issue
No description provided.
Delete branch "docs/imap-notes-plan"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Adds
docs/plans/0543-imap-notes.mdfor two-way Apple Notes in calternaldav before any product code. The design retains exact source and sent bases, preserves pending Apple edits as versions, prevents replacement expunges from deleting newer work, and excludes hidden content from every IMAP read path.Covers 100k notes per account, 10k-line notes, attachment-heavy notes, multiple devices and accounts; operation costs and two-second online visibility gates; crash/offline/conflict recovery; per-device app passwords; mapping properties, real-library IMAP conformance, native macOS VM acceptance, and scale tests in the first code PR. Rendering-only Apple features remain read-only where edits cannot round-trip. Actual MIME transfers and Apple save timing are included as explicit latency constraints.
Validation:
git diff --cached --checkpassed.Related to #543. Design proposal for review, not an implementation or measured performance claim.
Independent design review — Apple Notes over IMAP
Verdict: revise. Round 1 of at most 3.
Reviewed PR #766, branch
docs/imap-notes-plan, heada4230326f54f1131bc8eb172df779b09c3d10e40. All plan line references below refer to that head'sdocs/plans/0543-imap-notes.md.The direction matches kayg's decisions: two-way notes, server authority and history, no hidden export, read-only checklist state, base64 attachments, and no blind pending-edit overwrite. Retaining source and sent projections is appropriate. The first-code-PR scale requirement is explicit and should stay. The blockers are the operational rules between these invariants: several distinct Apple operations still map to the same unspecified receipt/guard machinery, and a required visibility primitive does not exist at this head.
Ranked blocking gaps and exact required edits
R1 — P1: make visibility an explicit prerequisite and define its publication boundary
Plan lines 19, 43–49, 66, 84, 89–91. “Use search's authoritative visibility policy” currently names a requirement, not an available implementation. At this head,
crates/notes/src/search.rs:151–179extracts title/tags and cleans every body line without filteringhidden:. The supportedhiddenvalue in ADR 0068 is a block-ID list; this plan also requires whole-note hiding but does not specify its accepted representation or how it composes with block hiding. Do not inherit whatever a permissive YAML getter happens to return.Concrete failure: export a visible note with a hidden paragraph and hidden attachment, then edit only that paragraph. If the source revision drives Message-ID, Date, a signed base header, UID or MODSEQ, an IMAP observer can detect the hidden edit despite unchanged visible content. More directly, a slow
BODY[]or partial attachment FETCH can already hold an old readable generation when Hide commits. Invalidating the cache and checking authorization at command start does not settle what can still leave the socket afterward.Required plan edits:
R2 — P1: specify Apple's actual move/delete/APPEND state machine, not only abstract MOVE
Plan lines 15–17, 45–49, 75–85. The older code is not the rewrite architecture, but its captured client behavior is relevant evidence.
calternal-v0/docs/research/apple-notes-imap.md:150–188records a Notes folder move as APPEND in the destination, then STORE Deleted/UID EXPUNGE in the source; it explicitly saw no UID MOVE. The new #543 probe verifies folder creation/rename and replacement saves, not an updated complete move/copy/delete matrix. The plan specifies identity-preserving MOVE, identity-changing COPY and same-note replacement receipts, but not the observed cross-mailbox APPEND sequence.Concrete failure: a device moves note X from A to B by APPEND, disconnects, then deletes X's source UID after a concurrent edit or another move. Depending on how the unspecified receipt is matched, the server can trash X, restore A, duplicate X, or treat a real copy as a move. A durable receipt alone does not define which result is correct.
There is also a protocol/semantic boundary to settle. “Repeat identical input returns its original result” can mean valid action deduplication, but APPEND has no standardized idempotency key. It cannot generally mean returning an old or expunged APPENDUID for every later identical APPEND, especially across a UIDVALIDITY reset. RFC 3501 §6.3.11 and RFC 9051 §6.3.12 append a new message; RFC 4315 §3 describes the UID assigned in the destination mailbox. Likewise, an IMAP COPY copies a message, while a Calternal copy needs a fresh identity graph. Specify any subsequent canonical reconciliation rather than silently rewriting the copied message under its assigned UID.
Required plan edits: add one compact transition table covering same-folder replacement, cross-folder APPEND move, explicit MOVE, COPY, standalone delete, cancelled deletion (
-FLAGS Deleted), failed APPEND followed by cleanup, and disconnect at each command boundary. For each transition give:Separate message-level APPEND behavior from deduplicating the canonical action. Distinguish an echo/retry from a deliberate edit that returns the note to an earlier visible value. Do not claim exactly-once client intent from content hashes alone. Measure the native move/copy/delete sequences before committing to folder semantics; a one-folder first slice avoids much of this ambiguity.
R3 — P1: prove base provenance early and bound all candidate-base work
Plan lines 19, 35, 47, 61, 78, 95–105. The signed-base fallback is correctly conditional, but its successful path is unverified. The #543 imported/edited pair contains the ordinary UUID/creation-date headers; it did not test an added signed base header or HTML binding attributes. The raw conflict samples also share
X-Mail-Created-Dateacross the base, server replacement and Apple edit. The older research/code explicitly warns that a newer FETCH is not the edit base and that creation dates survive multiple saves. These are reasons to keep the conservative fallback, not evidence that the proposed automatic path works.Concrete failure: an Installation has fetched hundreds of revisions, including repeated visible paragraphs whose private block identities or metadata differ, then reconnects with an edit made weeks ago and no retained custom header. Comparing all “plausible” bases is not the advertised O(S+A+B) operation. If a deadline expires, a conflict is safe, but ordinary editing after a long history can become permanently conflict-only. Truncating the candidate set and calling the surviving candidates unanimous is unsafe.
Required plan edits:
R4 — P1: define quota/full-disk outcomes across the durable stages
Plan lines 37, 45, 61–65, 74–85, 89. Quotas and streaming are named, but quota-full/ENOSPC is absent from the failure table. A crash-replay recipe does not by itself handle a permanently unavailable write target. “Keep raw incoming MIME,” “retain both byte sets,” and “no success before C/D” need a stated limit when there is no space to retain the incoming version.
Concrete failure: two concurrent 100 MiB attachment APPENDs each pass a preflight check, then jointly exhaust quota. Or B commits a receipt and raw version, C changes the room/history, and D fails because the mailbox database/WAL is full. An unqualified replay-before-reads barrier can then make the account unavailable indefinitely. Retrying from Apple can also amplify retained raw/conflict data.
Required plan edits:
crates/notes/src/history/blobs.rs:12–18,113–128recognizes vCard PHOTO lines).R5 — P2: define protected semantics and format-owner composition
Plan lines 19–35, 47, 95–105. A source companion makes byte preservation possible, and conservative rejection is valid. It does not establish that all accepted text/list/link operations preserve Calternal meaning. “Unchanged protected structure” needs to include the semantic graph, not only an HTML/Markdown tree.
Concrete failures: an apparently ordinary text edit changes a relationship link into/out of a standalone child; reordering a task/event list changes ownership; deleting a visible parent would also consume a hidden child; editing a contact list value races a CardDAV update while each adapter updates only its own companion. A generic room merge is not automatically a semantic compare-and-swap. At this head
crates/notes/src/room/edit.rs:84–118forks/merges a known-base patch and has a format-owner rebase hook for a matching staged companion. The history store supports companions per owner, but an individual API edit carries one optional companion; cross-format atomicity needs an explicit integration rule.Required plan edits: define the protected semantic inventory (IDs/retirement, hidden membership, relationship/owner bindings, task/event facts, frontmatter, table-width associations and reference-link definitions). Have the owning format modules validate the candidate against the current generation inside the guarded action; replan or retain a proposal if that generation changes. Cover notes already claimed by contacts/tasks/calendar and simultaneous changes through those protocols. State which such fields are editable through IMAP and how their companions are updated coherently; unsupported structured edits can be read-only in the first slice. Do not allocate public IDs merely to make the mapper easier.
Mapping audit: what “lossless” can actually mean
There is no implementation in this PR to execute. These are design counterexamples and required vectors, checked against the existing native MIME evidence, not claims of new Apple feature tests. Byte-exact preservation of unchanged source is achievable with a trusted matching companion; full editable Apple feature parity is not. Lossless source retention and an editable projection must remain separate acceptance checks.
[/],[-], unknown boxes and hidden children. Disabled HTML controls still need a native save test; observed checkbox clicks are not durable state.applenotes:rendering did not prove navigation.I independently decoded the supplied #543 samples: both recorded PNG paths retain the original 209 bytes and SHA-256; the recorded PDF changes from 599 to 631 bytes and exactly equals LF→CRLF conversion. The provided evidence does not establish outgoing-base64 PDF survival through a later Apple save, arbitrary attachments, binding/header retention, nested-list/cell editing, or every failure ordering.
Protocol requirements to add to the acceptance checklist
Normative references: RFC 3501, RFC 9051, RFC 7162, RFC 2177, RFC 4315. Native evidence: #543 report, raw MIME, and earlier Apple trace.
The UID-per-immutable-message model is correct. Complete the protocol contract around it:
CHANGEDSINCE 0seen in the older trace..SILENTMODSEQ responses, no-op flag changes, BODY FETCH setting Seen and BODY.PEEK not doing so. Do not infer Apple's use of a conditional STORE from the server advertising CONDSTORE.async-imapsocket tests are useful; add scripted raw-wire cases for invalid literals, pipelining, partial FETCH/HEADER sections, empty UID sets, CLOSE, failed writes, capability negotiation and reconnect. Captured Apple replay plus RFC invariants is stronger than merely accepting one library's successful requests.Security and isolation assessment
The per-Installation Notes-only app-password design, TLS, live revocation, and user/account-scoped lookup keys are appropriate. Make the two-user/two-account test cover every command and code path: authentication, LIST/LSUB/STATUS, SEARCH predicates and counts, FETCH headers/sections/attachments, APPEND UUID aliases, COPY/MOVE, flags/expunge, partial replies, receipts, reconnect, recovery and submission compatibility. A Notes credential must not gain web/Mail/Contacts access or another account through an alias, shared hash or cache hit. Revocation must fence in-flight reads and mutations as well as wake an idle connection. Invalid foreign identities must not expose titles, paths, blob availability or another user’s deduplication result. The signed base, if used, must be verified under the authenticated account; an Apple UUID is a claim, not authorization. R1 is the remaining concrete hidden-content blocker.
Failure-table additions / required outcomes
The existing A–E crash rows and preserved-conflict policy are useful. Extend them with these concrete sequences and assertions:
Cost, scale and simpler delivery
The budgets are correctly called gates, not results. The plan does require scale tests in the first code PR (lines 101 and 105), which directly addresses the earlier small-fixture failure. Keep that requirement and make the harness non-vacuous:
A simpler first vertical slice is: native provenance experiment → one Notes mailbox → safe text/style edits on known bases, source-preserving display of unsupported structures, base64 verified attachments, durable conflicts and hidden enforcement → two-device/crash/scale acceptance. Keep true two-way editing as the milestone; defer nested-folder mutation, identity-changing COPY, broad structural edits and unproved media affordances until their evidence exists. QRESYNC can land after a correct persistent UID/CONDSTORE/IDLE path if not required by the captured client; advertise it only when complete. This reduces moving parts without changing the owner's final scope or reintroducing public IDs for every block.
What must be verified on macos-01-std first
Before committing to the mapper/receipt architecture, capture redacted wire plus original/returned MIME for:
mdfindresult establishes neither UI search nor removal. Keep the existing cleanup/reservation rules.These are required experiments to put into the plan, not tests run by this writing job.
Review evidence and validation
Read issue #543 and all 21 currently returned comments, including its plan announcement; downloaded/read the macOS 27 report and examined the supplied raw MIME archive. Read ADRs 0028, 0056, 0062–0064 and the relevant 0066–0068 format/sync contracts; examined current notes/search/history/companion/attachment/action code, the unmerged
origin/issue/carddav-readprojection, and earliercalternal-v0IMAP/DAV source/research atebaa86165145f871441efe5ae950c7cfc94cc953. Read the available global and v0CLAUDE.mdcontracts; there is no trackedCLAUDE.mdin the reviewed rewrite tree. Checked RFC 3501/9051 message/command/session rules, RFC 7162 CONDSTORE/QRESYNC, RFC 2177 IDLE, and RFC 4315 UIDPLUS.Worktree:
/home/agent/wt/review-766, freshly created detached atorigin/docs/imap-notes-plan, verified heada4230326f54f1131bc8eb172df779b09c3d10e40. Review artifact:/home/agent/codex-jobs/review-766/review.md. Evidence-check output:/home/agent/codex-jobs/review-766/evidence-check.txt.No product code changed. No build, heavy run, native feature run, deploy, push or merge was performed. The native checks above remain unverified requirements. This review requests concrete plan edits; it does not claim measured implementation failures for code that does not yet exist.
Round 2 revision pushed as
a9eeb2c9(documentation only), addressing review 69752:Also expanded the mapping/transport vectors, rev1/CONDSTORE/ENABLE-QRESYNC/IDLE/epoch/tombstone contract, all-command account isolation/revocation checks, and failure sequences. Scale includes H/C, bounded queues/connections, whole-store growth/shared attachment payloads across 52 edits, the full first-code-PR fixtures, explicit 1 TiB fixture policy, total/incremental memory, initial-use versus metadata/full delivery, and actual wire bandwidth/device rendering. Delivery is verification spike → pure format/storage scale harness → guarded visibility/actions → one-mailbox usable two-way slice → evidence-gated breadth.
Validation:
git diff --check, local Markdown links, review-item coverage self-check, and a five-page A4 print (10pt prose, 9.25pt tables, 12.7mm margins). Onlydocs/plans/0543-imap-notes.mdchanged. No product code, native experiment, heavy test or deploy was run; slice-0 observations remain required work, not claimed results. Ready for the next independent review (maximum 3 rounds).Independent design review — Apple Notes over IMAP, round 2
Verdict: approve with required edits. Round 2 of a maximum of 3.
Reviewed PR #766,
docs/imap-notes-plan, heada9eeb2c9cdf88a637f1852eff9739ead3b899d03. Line references below are todocs/plans/0543-imap-notes.mdat that head. Read the round-1 review, including its mapping/protocol/security/failure/cost sections, and the author's itemized response.The revision closes the broad design gaps. Visibility now has a publication boundary; source preservation is separated from editable parity; cleanup is scoped to immutable messages; provenance work is bounded without sampling; capacity and owner composition have explicit prerequisites. Conditional support and private proposals are honest limits. I do not require implementation or completion of the native spike to approve this documentation PR. Two bounded plan edits remain; neither requires another architectural rewrite.
Exact required edits
E1 — P1: distinguish a definitively rejected APPEND from an uncertain completion
Lines 62, 73, 85, 89 and 93–97. The universal rule that all capacity failures return
NO [OVERQUOTA]conflicts with the post-B recovery rows, which preserve redo and later retry the action. “No success” does not specify whether a tagged failure was sent.Counterexample: B durably records an Apple edit; C commits its source/history; D fails on a full projection WAL. Line 89 permits
NO [OVERQUOTA]. Apple retains/retries the failed edit or lets the user undo it. After repair, recovery publishes D from the rejected command. The user now has a late applied edit after a definite failure, potentially followed by another retry. The same problem exists if a B-only action receivesNOand is subsequently committed by recovery.RFC 3501 §6.3.11 requires an unsuccessful APPEND to restore the mailbox to its previous state; no partial appending is allowed. RFC 9051 §6.3.12 repeats that rule, allowing a UIDNEXT gap. Retaining a private rejected proposal is compatible with failure; replaying it automatically into the live note/mailbox is a different outcome.
Required edit: add terminal
aborted / committed / indeterminateoutcomes to §4 and qualify line 89:NOonly after the operation is durably aborted, with no later automatic canonical mutation or mailbox publication from that attempt. Retained private evidence can remain available for explicit resolution.OK [APPENDUID ...]; losing that reply remains uncertain to the client.NO→ never later applied and lost completion → safe recovery and subsequent fresh operation.This uses the existing journal/publication mechanism. It does not require a second transaction system or rollback of acknowledged work. Capacity causes can still be hidden from ordinary users.
E2 — P2: make slice 0 observe the new reconciliation paths before committing to them
Lines 9–19, 62, 70–75 and 125. The spike covers native commands extensively, but it does not explicitly reproduce the new server behavior: admit exact raw MIME at UID R, then expunge R and publish canonical MIME at UID C, including when the incoming edit became only a private proposal. Line 75's deletion-loop check is currently later acceptance, although this behavior decides whether the chosen receipt/republication strategy is usable.
Counterexample: Apple receives
APPENDUID R; the bridge retires R and republishes the unchanged canonical note at C because the edit conflicted. A dirty client treats C as another competing replacement, saves again, and cleans up C. The bridge preserves the note and republishes again. No source data is lost, yet the client and bridge can keep saving/republishing, or the Apple user can lose access to the pending local text after an apparently successful save. #543's ordinary external replacement and pending-edit race do not establish convergence of this new path.Required edit: add a compact adversarial observation group to slice 0, with these explicit cases:
For each, require exact wire/MIME and UI evidence, not just absence of a server crash. State the branch: no proven convergence/recoverability means defer the affected write/delete path or stop the two-way milestone; no guessed cleanup or new heuristic can substitute for evidence. These are plan additions, not a request to run the experiments in this writing job.
R1–R5 disposition
The mapping table now addresses the material round-1 counterexamples: real nested/repeated lists and cell edits, code whitespace, source-marker privacy, strict transport decoding, opaque-content edits, and attachment byte verification. None of those rows is evidence of native parity until the spike supplies it. That distinction is maintained correctly.
Failure table exercised on paper
I found no additional blocker in the rev1/CONDSTORE/QRESYNC/IDLE contract. The plan correctly treats CONDSTORE as metadata concurrency, requires ENABLE before QRESYNC behavior, distinguishes VANISHED from EARLIER, preserves immutable UID epochs across active sessions and bounds tombstone retention. Sequence-safe notification restrictions still apply during reconciliation; IDLE cannot promise an unselected folder's delivery deadline. Test advertisement/grammar only for capabilities actually implemented. The proposed RFC 9051 use as guidance is appropriate for a rev1 server.
Account/Installation scoping, signed-token verification, all-command isolation, revocation during reads/mutations, private companions/proposals and restricted submission are sufficient design requirements. I am not reopening security work already made explicit in this revision.
Simplicity and the first slice
The one-mailbox, unclaimed-note, text/style boundary is a meaningful simplification. Deferring folder intent, COPY identity changes, owner-composed structure, QRESYNC and media until evidence exists is appropriate. Do not broaden that boundary merely because the vector table names richer formats.
The first post-spike slice is valuable as a feasibility harness, especially given the mandatory first-code-PR scale/amplification gates. It is not yet a small user-visible slice: the first usable milestone is still slice 3. That is acceptable for this plan, but the least costly implementation order is outbound authorized projection + immutable MIME/storage + byte-exact no-op companions first, then safe inbound editing. A one-way-out acceptance checkpoint within the protocol slice would establish real device value before enabling inbound merges; unsupported Apple writes must receive explicit, tested behavior. This is a sequencing recommendation, not a third required edit or permission to weaken the final two-way scope.
Keep the first-PR scale requirement and the distinction between pure-harness results and native acceptance. The H/C variables, bounded queues/trees, total-process memory, logical-versus-physical storage accounting, streaming 1 TiB fixture policy and measured ~137 MiB wire cost are now explicit. The two-second gate remains demanding for heavy fixtures; it is honestly conditional on measured network/device performance, not evidence already achieved.
Evidence and scope
Fresh detached worktree:
/home/agent/wt/review-766b, created atorigin/docs/imap-notes-planand verified asa9eeb2c9cdf88a637f1852eff9739ead3b899d03on 10by32-01-std. Review artifact:/home/agent/codex-jobs/review-766b/review.md.Read #543 and all returned comments, the macOS 27 report and raw-MIME archive, the prior review and author response, the revised plan, relevant visibility/room/companion context and ADR 0062/0068. Re-fetched RFCs 3501, 9051, 7162, 2177 and supporting UIDPLUS RFC 4315 from the RFC Editor; checked APPEND failure/UID immutability, STORE/EXPUNGE/CLOSE, CONDSTORE/ENABLE/VANISHED/tombstone and IDLE rules. Re-fetched the report/archive; both match the round-1 copies byte-for-byte. Independently decoded the recorded PDF/PNG samples: PDF is 631 bytes with recorded hash
b39b06fe…; both PNG paths retain 209 bytes and hashc29de0e1…. The conflict samples preserve the same UUID/creation date, not proof of a newer edit base.No product code, plan file, commit, push, merge, deployment, build, heavy run or native feature test was performed. This approves the design subject only to E1 and E2; the native/scale gates remain unrun work, not review claims of implementation correctness.
Applied the two required round-2 edits from review 69759 in
0816b5ff72cec14b56e39eb0fbc24803f9788f21. Documentation only; no product code or native experiments.E1 — §4 APPEND outcomes and retry reconciliation (quoted):
E2 — §1 slice-0 native reconciliation/recovery observations and branch decisions (quoted):
Matching §3 command/failure rows now distinguish definitive rejection from lost completion. §4 failure rows cover durable abort before NO, B/C uncertainty, no replay after rejection, lost-reply recovery followed by a fresh operation, raw/canonical convergence, epoch resets and offline history budgets.
Validation:
git diff --checkpassed; onlydocs/plans/0543-imap-notes.mdchanged; the existing five page sections/four explicit page breaks are retained. Native/scale gates remain planned experiments, not claimed results.Slice 0 preflight on 6by32-compute01 is blocked; no native experiment ran. Read the approved plan at
0816b5ff72cec14b56e39eb0fbc24803f9788f21, both reviews and their responses before preflight.Findings: docs/plans/766-slice0-findings.md, branch
spike/imap-766-s0, commit2b16363d.macos-01-std.vpn.kayg.orgresolves to10.69.69.216; a FQDN TCP/22 probe returnedSSH-2.0-NetBird-SSH-Server-0.82.0. This establishes listener reachability only. The inspected plan/current dev docs contain no macOS SSH username; no login was guessed and no authenticated SSH command was attempted. This is not an observed VM-to-VM SSH failure.opis absent from PATH and~/.config/1password/calternal.envis missing. The vault was not queried; whether a suitable disposable account exists remains unknown.Needed to resume: documented macOS SSH login and access to the specified 1Password setup on the execution host (or control-host execution with those available). The document specifies the disposable trusted-TLS IMAP account and controllable harness/two-client requirements; no secret should be supplied in comments.
Validation: staged
git diff --checkpassed; only the findings document was added, based directly on the approved plan head. Branch pushed. No Rust builds, product changes, Mac lock/resources, GUI login/logout, trust/certificate changes, merge or deployment.Slice 0 control-host continuation: blocked on a suitable disposable IMAP account; no native experiment ran.
Continued
spike/imap-766-s0from2b16363d; pushed documentation-only commit7b02b7b. Updated findings contain metadata-only commands/output, all ten baseline/E2 groups marked unrun, and exact resumption requirements.The specified 1Password setup now works.
Apps / Calternalreturned four items: YNAB API KEY, root@calternal-cloud, Unsplash API Key, and Apple Certificates. None is identified as a disposable IMAP/Notes test account. The cloud-server login is not authorization to experiment on a mail account. Stopped at the requested no-account gate; no credential values were printed or saved.Needed: an explicitly disposable Apple Notes-capable IMAP account in that vault, separate from personal mail, with hostname, TLS port, username/password, Notes mailbox namespace and any required submission/profile details. TLS must validate against existing guest trust. The full spike also needs a controllable endpoint/harness for every-connection traces, MIME/base injection, capability variation, raw/canonical reconciliation, quota failures, dropped completions and UIDVALIDITY resets; two-client cases need a second real Apple installation. Put secrets only in 1Password.
The guest login/direct-IP route and Studio fallback are now supplied, superseding the old missing-login prerequisite, but neither was attempted because account preflight stopped first. No Mac lock/resources, SSH, GUI interaction, trust/certificate changes, Studio filesystem access, /mnt/mac access, product code, merge or deployment. Neither osascript nor cua-driver was tested. All feasibility gates remain unresolved.
Validation:
git diff --checkpassed; onlydocs/plans/766-slice0-findings.mdchanged. No product builds/tests were needed for this documentation update.Disposable-endpoint continuation of slice 0: blocked before any native IMAP experiment. Pushed documentation-only commit
349ad3dd0e13a06e9da5491be23cdf3e378806d8tospike/imap-766-s0.Updated findings record commands/errors, cleanup, all ten groups still unrun, and resumption prerequisites. The vault-account prerequisite is superseded by the authorized disposable endpoint.
agent@6by32-compute01.vpn.kayg.org. Wildcard cert/key files exist with mode 0600. Podman is absent from PATH; Nix supplied Podman 5.8.9. Rootless initialization fails because privilegednewuidmap/newgidmaphelpers are missing. Adding Nix shadow to PATH instead failsnewuidmap: write to uid_map failed: Operation not permitted, with Podman's setuid/filecap diagnostic. Subordinate UID/GID ranges exist.sudo -n truerequires a password. An isolated unprivileged namespace probe reportsRootless=false; it was not used to deploy Dovecot./tmp/mac-bench.lock:calternal bm1-756 2026-10-30T13:08:30Z 90(18:38:30 IST). Its lock was left intact. Waiting stopped once the independent server blocker was established.calternal-01-std(iOS 27.0), but its full installed-app list contains no Notes or Mail; Settings search also finds no Notes. Multi-client cases need another Notes installation.Needed next: install working privileged UID/GID mapping helpers on the Debian VM (normally
uidmap), verify Podman runs rootless asagent, then provision/test trusted TLS and rawlogging before acquiring the Mac lock. No mapper/receipt/delete/recovery feasibility gate has passed.No container/image/mailbox/account/profile/credential file was created; no IMAP rawlogs exist because no session started. The isolated Podman probe directory was removed. Nix shared-store downloads remain cached. No trust changes, GUI login/logout, product code, merge or deployment. No
/mnt/macaccess or direct Studio/Usersshell access beyond the specified lume binary; T3 managed its own simulator runner/session artifacts. Validation: stagedgit diff --checkpassed; only the findings doc changed; remote branch SHA verified.Slice 0 (macOS spike) ran partly and is now paused at the orchestrator's request. Pushed
f490ae6tospike/imap-766-s0(docs only): findings, redacted evidence. No product code, no merge.Setup that worked: stock Dovecot 2.4.5 run as
agenton 6by32-compute01 (no containers), IMAPS on 9993 with the wildcard cert. The macOS 26.5.2 guest (Notes 4.13) trusted it with no trust changes. Full rawlog plus a TLS tap for fault injection. Only one Apple client existed; two-client cases were not run.Answered (with wire evidence)
UID STORE \Deleted+UID EXPUNGEof the last UID Apple knows. Moves are APPEND(dest) + STORE/EXPUNGE(src), not UID MOVE. Delete = STORE/EXPUNGE with no trash copy.display:nonetext leaks into the Notes list preview.text/*is rewritten (LF→CRLF, CRLF→CRCRLF) and empty files become AppleDouble.NO [OVERQUOTA]→ Apple keeps the text locally (survives restart), does no cleanup, and sends the whole pending text with the next edit. A lost APPEND/STORE completion leaves permanent same-UUID duplicates /\Deletedleftovers that Apple never cleans.UID STORE/EXPUNGE 27deleted an unrelated note. The plan needs numeric UIDs never reused across epochs.host:port, needs an Outgoing server filled in, and never contacted my SMTP stub.Not answered: nested-structure/10k-line round trips, UIDPLUS-off / IDLE-off / Unicode folders / literal grammar / revocation, hide + Spotlight + Siri, copy, undo, attachment removal, any two-client case, E2-2 proposal + stale cleanup, E2-3 EXPUNGE-drop and crash/repair, E2-4 active client, E2-5 (tokens vs ledger, >64 bases) entirely. No feasibility gate is closed by this run. Section 6 of the findings lists the 13 plan changes the observations support; none are applied to the plan doc.
Cleanup (verified): test account deleted in System Settings (Accounts DB holds only the pre-existing rows; no keychain items); Notes/Mail/Reminders/Settings quit; Mac job dir removed. On 6by32-compute01 all four
imap-766*units stopped, ports 9993/9995/9465 closed,~/imap-766and temp files deleted./tmp/mac-bench.lockreleased at 17:26:16Z (22:56 IST) and the dir is gone. Left behind: the shared Nix store copy of Dovecot; the lume user's one-time Automation approval for Notes (user scope); Mail's first-run privacy choice; control-host scratch in/tmp/imap766(scripts + screenshots, no secrets).Resume: the scripts in
evidence/tools/rebuild the rig (needslib.py, a ~25-line imaplib helper that connects by IP with SNI because the server's local DNS is flaky). Next best slice: E2-5, a fast-second-edit race for E2-1, then the two-client cases with a scripted secondimaplibconnection.Slice 0 continuation (2026-10-31 UTC)
cal-basecomment ordata-calcarriers. The cut fired before edit 5's cleanupUID STORE 341; after reconnect, edit 6 appended UID 344 and cleaned canonical UID 343. The test actor left live canonicals 341 and 353 because it does not supersede earlier copies.UID STORE 363andUID EXPUNGE 363returnedOK, the actor skipped R1 and published R2 UID 364 as C365. Notes showed both edits and one live copy. The 0.3 s / 0.35 s attempt also missed the R1 target: Apple cleaned C1 UID 368 after R2 UID 369.STORE/EXPUNGEbothOK). Final Notes-316 containedP1 by BandA1, with one live copy.OK); actor published C373. On reconnect, Mac appended R374 and again cleaned old UID 371 (OK); actor published C375. After five minutes, live UIDs 373 and 375 held disjointP2 by BandP1 by A; Notes showed only A's text and no proposal.P3 by A; the unrelated synthetic note remained. B'sSTORE/EXPUNGEboth returnedOK.UID STORE 377/UID EXPUNGE 377, bothOK. The UUID stayed absent for the five-minute poll; Notes showed zero copies. The actor logged no further republish after B's delete.imaplibclient, and the actor did not log a second cycle.S0 keep/S0 editto UIDs 400 / 401; new UIDVALIDITY 1793424367. Mac's offline edit appended UID 402; stale cleanupUID STORE 379/UID EXPUNGE 379both returnedOK. Actor superseded 401 and published C403. Final live UIDs were 400 and 403; both notes and the pendingP3 by Atext remained.CAPABILITY False False;IFJN3 BAD Unsupported extension,IFJN4 BAD Unsupported extension,IFJN6 BAD Unsupported extensionforENABLE QRESYNC,SELECT ... (CONDSTORE), andUID FETCH ... (CHANGEDSINCE 0).Open and skipped items
delete accountcall failed with-10000; System Events returned-1743(Not authorized to send Apple events to System Events). TheExampleaccount's synthetic notes are deleted, but removing the account requires local GUI authorization in System Settings → Internet Accounts. I did not force that over SSH.Cleanup
On dev-02, Dovecot and the tap are stopped; ports 9145 and 443 are closed. I removed
/home/cua/imap-766, including throwaway users and credential files. On the Mac, all synthetic Notes are deleted,/tmp/imap766-jobis removed, and the lock-holder shell exited through its EXIT trap. The emptyExampletest account remains for the GUI-removal reason above. No real mail account was touched.Fresh-mailbox recovery: scripted/native status (2026-10-31 UTC)
Three variants were exercised with scripted IMAP clients against disposable Dovecot 2.4.5. No Notes.app account was configured. See the findings note, redacted transcript, and summary.
LISTshowed both names and all three UUIDs appeared in both. The fresh name enteredLSUBonly after explicitSUBSCRIBE. A scripted pending-edit APPEND to the old mailbox left the fresh copy at its base text.SELECTof the old name returnedNO [NONEXISTENT]. A session already selected on it gotNO [NONEXISTENT]for staleSTORE, then itsUID EXPUNGEfailed with a relogin/session-state error. Deleting the retired name left the fresh server mailbox’s three notes intact.NO [NONEXISTENT]. In a writable fresh selection, replaying old UID 2 as UID 2 returnedOKforSTORE/EXPUNGEand removed a different synthetic note assigned UID 2 in the fresh mailbox.Native Notes behavior remains unknown: no Notes folders, duplicate presentation, re-download, stale-UID action, or local-only offline edit was observed. Setup stopped before account creation because temporary CA trust configuration returned
SecTrustSettingsSetTrustSettings: The authorization was denied since no user interaction was possible.Keychain Access launched without a visible window. A scripted pending-edit MIME explicitly APPENDed to the fresh mailbox remained beside the base message, but this does not test local Notes cache handoff. The native recovery gate in round-2 review remains open; an interactive Keychain authorization path (or pre-trusted test CA) is needed to rerun it.GUI prerequisites on
macos-01-stdare done (2026-11-01, IST morning):mkcert.sh), and slice 0 used a public wildcard cert. Trusting needs no GUI:~/bin/trust-test-ca.sh <ca.pem>on the Mac (sudo security add-trusted-cert -d -r trustRoot -p ssl -k /Library/Keychains/System.keychain). Proved with a throwaway CA: verify-cert NOT_TRUSTED before, "successful" after; removed again afterwards. Remove a run's CA at the end withsudo security remove-trusted-cert -d ca.pem.Screen Sharing is enabled on the VM. Reconnect: GUI clicks are easiest through the guest's cua-spacesd (MCP at 127.0.0.1:3211, token in ~/.cua/spacesd/token). The VNC framebuffer showed black, so VNC is not useful here.
Native fresh-mailbox recovery evidence, 1 November 2026
Pushed
fbf69c2f2622ccd91f3f21049ce1c16f6d194f11todocs/imap-766-plan-v2(plan PR #775). Native report and redacted evidence; the parent evidence README is updated.One real macOS 26.5.2 / Notes 4.13 client against disposable Dovecot 2.4.5 on dev-02, approximately 12:07–12:23 IST. Three synthetic UUIDs and throwaway credentials only. Scripted server transitions, native Apple saves/sync/cache observations; no Calternal reconciliation actor or second Apple client.
Notes-766-activewas not discovered/downloaded, even after Notes relaunch. Notes showed one folder/three notes and listedNotes/*, not arbitrary root names.Notes, receivedNO [NONEXISTENT], and kept its cached three notes and prior active edit. Fresh copies/bystanders were unchanged. Server removal did not migrate or remove the cached folder.UID STORE 20002/UID EXPUNGE 20002. Old cleanup was a no-op and bystanders survived. Server had four messages, including base/pending A with the same UUID; Notes showed three and exposed base A rather than the uploaded pending text. Raw pending text was retained, but hidden from this native view.Notes, including after retired-folder deletion and Notes relaunch. Notes retried old SELECT/STATUS, never selected the fresh name, and did not transfer the pending text. Fresh server mailbox remained three base messages. Final native observation was about two minutes after retirement.UID conclusion: higher numeric UIDs prevented the measured stale-cleanup collision. Changed UIDVALIDITY did not stop Notes using an old cleanup number, even after its new SELECT response. This supports never-reuse with complete independent issued/reserved-range authority; it does not prove safety for unknown restored history, or native cleanup after selecting a different mailbox name (Notes never selected the fresh roots here). No wrong-note edit or bystander deletion occurred.
Round 3 changes needed: transparent
Notes-<generation>root-name recovery fails this client. Keep incomplete-history writes stopped until a tested account re-pairing path exposes the fresh account namespace at the expected mailbox; re-pairing itself is still untested. Keep visible pending-text/conflict reconciliation as an explicit native gate: server retention plus higher UIDs did not preserve visible access after the offline same-name reset. Export/reconcile pending text before account removal, which warns it removes Notes data. Do not close recovery or claim transparent migration from this run.Validation on dev-02:
nix develop -c bash -c 'python3 ~/imap-766/tools/analyze.py && python3 ~/imap-766/tools/export.py'passed. It checked 60 captured bystander instances unchanged (two distinct bystanders), increasing observed UID ranges, the duplicate raw A state, zero native fresh-root selections, and absence of throwaway passwords in evidence/archive members.git diff --checkpassed; pushed SHA verified. Evidence includes all-connection redacted wire/rawlogs, stored MIME, native folder/body snapshots, screenshots and exact harness/check scripts. No product code tests or native recovery release gate are claimed passing.Cleanup verified: test account removed; Internet Accounts says
No accounts, Notes reports onlyOn My Mac; CA trust/certificate removed and verification no longer trusts it; no test endpoint/user keychain item; Mac tree and EXIT-trap lock gone. Both server units inactive, ports 993/9145 closed, disposable server/credential tree removed. Pending bodies were exported before account deletion. No production service or real account/note touched.kayg referenced this pull request2026-11-05 23:21:11 +00:00
housekeeping #1009: rebased+green, left open for review.
rebased IMAP plan while retaining proposed recovery decisions.
head:
42a690e21af707b35256b1f411bc998a3d18f5e6. rebased onto devcd7f84432febfc63b788fbb9b9f9950452982d74and pushed with the original exact force-with-lease.validation on the Mac VM, through Nix and the shared Mac lock/slot: root
bun install --frozen-lockfile; inapps/web,bun run check,bun run lint,bun run build,bun run test;PERF_DIST=<worktree>/apps/web/dist bash perf/web.sh --bundle-only --gate. all passed. this documentation-only branch has no targeted browser specs or Rust implementation changes. exact commands, logs, and dev bundle control.original-head missing-commit/patch inventory, file
pr-766.md.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.Merge
Merge the changes and update on Forgejo.Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.