# 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.
