# Known limitations Honest list of where `@mac-sync` is fragile or incomplete. Verified against the code as of the current tree. ## iMessage `attributedBody` decoder is heuristic, not a full typedstream parser `Reader.swift:371` (`extractTextFromAttributedBody`) is now wired into the row extraction loop: when chat.db's `text` column is null/empty AND `attributedBody` is non-nil, the byte-scanning heuristic recovers plain text from the NSKeyedArchiver typedstream and uses it as the message body. This covers most URL bubbles, reactions, and expressive sends. What it does NOT do: - Decode rich formatting (bold/italic/colors). Only the raw text run is extracted. - Handle multi-run NSAttributedStrings — only the first contiguous text run with an `NSString` marker is returned. Multi-paragraph rich messages may lose later paragraphs. - Decode binary attachments embedded in the blob. The full typedstream decoder on the server side is a stub at `src/server/src/shared/typedstream/decode.ts` that returns `null`. When implemented, it will populate `icloud.messages.body_decoded` for messages where the Mac-side heuristic failed or where historical data needs a backfill pass. The schema (`attributed_body BYTEA` + `body_decoded TEXT` columns + combined FTS tsvector) is already in place. ## iPhoto has no Sender By design (`~/.claude/plans/magical-tumbling-peach.md:184`): there is no 2-way Photos sync. iPhoto is Mac-to-server only. The dashboard cannot upload photos to the Mac library. Photos.framework's write API is awkward and the user explicitly scoped this out. ## AppleScript fragility Three modules talk to macOS apps via AppleScript: iMessage (Messages.app), iMail (Mail.app via ScriptingBridge), and iNotes (Notes.app). Known failure modes: - **iNotes attachments do not round-trip.** AppleScript's note `body` is HTML, but inline images and Apple Pencil drawings are stored out-of-band. The Notes Sender writes plain HTML only. Existing attachments stay; new ones cannot be created from the web. - **Notes formatting fidelity is lossy.** Bulleted lists, code blocks, and checkboxes survive a round-trip; pinned/locked state, tags, and per-paragraph styles may not. - **Permission prompts.** First run of each AppleScript module triggers System Settings > Privacy > Automation. If the user denies, the module's `onAuthorizationDenied()` hook sets the per-module error (e.g. `noteAccessRequired`, `automationDenied`) and the cycle bails out silently (`@packages/inotes/Sources/INoteSync/SyncManager.swift:91-94`). - **iMessage AppleScript can hit Messages.app stalls.** The `SendService` layer (`@packages/imessage/Sources/IMessageSync/Sender.swift`) wraps the AppleScript call with rate limits and delivery tracking but cannot recover from a hung `Messages` process — manual restart is required. ## Watermark is cycle-start time, not record time `BaseSyncManager.runCycle` sets `lastSync = Date()` at the end of a successful cycle (`@packages/shared/Sources/MacSyncShared/Sync/BaseSyncManager.swift:126-127`). If the OS produces records faster than the cycle runs, edge-of-window records can be missed. iMail's Reader compensates with a ±1-minute tolerance (`@packages/imail/Sources/IMailSync/SyncManager.swift:40-43`); other modules do not. In practice this only matters for high-frequency streams, which only iMessage really has (30s tick mitigates it). ## Conflict resolution: last-write-wins Per the plan (`~/.claude/plans/magical-tumbling-peach.md:182`): > iCloud-side conflict resolution for simultaneous Mac+web edits — last-write-wins > via `modifiedAt`. Document in code, not a feature. If you edit the same event from Calendar.app and from the web within the same cycle, whichever write completes second wins. There is no operational transformation, no CRDT, no merge UI. ## iMessage send queue is on a bespoke schema The legacy `icloud.send_queue` table predates the generic `createSendQueueRepo` factory (`src/server/src/entities/send-queue/schema.ts:5-23`). It has extra columns (`send_queue_id`, custom status enum) that the factory does not model. This is intentional — see plan line 12 and architecture invariant 2 — but it means tooling (admin dashboards, retry scripts) must special-case the iMessage queue. ## `IPhoto.Stats.uploadRate` is per-session only `IPhotoSyncStats.uploadStartTime` resets on every cold start (`@packages/iphoto/Sources/IPhotoSync/SyncManager.swift:14-46`). The dashboard shows live throughput during a session but cannot report historical upload speeds. ## iMail `/my/mail` exposes no folder browser Only `/conversations`, `/conversations/:id/messages`, and `/last-sync` exist (`surfaces/my/mail.ts:10-25`). The web tab cannot drill into folders by name. ## Server has no auth bootstrap for fresh devices `POST /client/devices/register` is exempt from `deviceTokenAuth` (`src/server/src/app/server.ts:69-71`). Anyone on the LAN who can reach `:3201` can register a device. The SSO middleware on `/my/*` protects the web side, but `/client/*` relies on network isolation. If you expose `:3201` to the public internet, register-spam is a real risk. ## iCalls limitations (Mac CallHistory only) - **WAL freshness**: recent calls live in the `-wal` sidecar and may not be visible to a plain readonly connection to `CallHistory.storedata`. The `CallHistoryReader` uses a per-cycle snapshot (`makeSnapshot` copies `.storedata` + `-wal` + `-shm` to a temp dir and opens read-write so SQLite replays frames). See `Reader.swift` and the WAL integration test in `ICallsSyncTests`. - **iPhone cellular calls missing**: the Mac DB only ever sees FaceTime + Continuity-relayed calls (`ZSERVICE_PROVIDER` = `com.apple.FaceTime` or Telephony relay). Plain cellular calls that ring only on the iPhone do not appear. No Mac-only fix exists; an iOS-side reader or CallKit event stream would be required. Documented in calls-for-triage handoff. - **0-duration semantics**: `durationSeconds == 0` + unanswered is common for short rings, missed FaceTime requests, or connection attempts. Real conversations have positive duration. - **Local `/api/calls`**: the Mac app's `LocalWebServer` exposes `http://localhost:8765/api/calls` (and `callHistoryDb` in diagnostics) for direct agent/tool access. This is in addition to the server `/my/calls`. - **MCP / triage**: `recent_calls` tool (in mcp/) and quinn-api consumers should use the server surface (`/my/calls?since=...`), not poke the local DB.