# iNotes (`INoteSync`)

## Purpose

Sync Notes.app notes with the server, and apply web-initiated note edits back
via AppleScript.

## Direction

Bidirectional via AppleScript — accepted fragility per
`~/.claude/plans/magical-tumbling-peach.md:108-117`.

## OS surface

Notes.app via AppleScript (`NSAppleScript`). No linker entry needed; the
target has no `linkerSettings` (`Package.swift:90-94`). Requires
Automation > Notes.

## Files

- `Reader.swift` — `public final class NotesReader` (line 49):
  `requestAuthorization()` (line 59), `fetchAllNotes()` (line 70). The fetch
  script reads `id, name, body, folder, modification date` from every note in
  every account
  (`@packages/inotes/Sources/INoteSync/Reader.swift:78-95`).
- `APIClient.swift`:
  - `syncNotes(_:)` -> `POST /client/inotes/sync`
    (`@packages/inotes/Sources/INoteSync/APIClient.swift:79`)
  - `getStats()` -> `GET /client/inotes/stats` (line 94)
  - `getPendingSends()` -> `GET /client/inotes/send-queue/pending` (line 109)
  - `reportSendResult(...)` -> `POST /client/inotes/send-queue/:id/result` (line 132)
- `Sender.swift`:
  - `NoteApplying` protocol (line 65) + production
    `AppleScriptNoteApplier` (line 110) and a `NoteSender`
    facade wrapper (line 76).
  - Scripts assembled by `scriptForCreate`, `scriptForUpdate`,
    `scriptForDelete` (lines 149, 163, 181) using
    `AppleScriptEscape.quote` for all interpolation.
  - `NoteSendTransport` adapts the typed APIClient calls into
    `SendQueueTransport` (line 231).
- `SyncManager.swift` — slow interval (600s) because AppleScript is
  expensive; `lazy var sendQueueClient: SendQueueClient<NoteSendTransport>`
  drains every 60s (`SyncManager.swift:54-72`). Batch size 100 (line 51).

## Timing

- Read interval: **600s**
  (`@packages/inotes/Sources/INoteSync/SyncManager.swift:79`).
- Outbound poll interval: **60s**
  (`@packages/inotes/Sources/INoteSync/SyncManager.swift:60`).
- Note batch size: 100.

## Server surface

- Entity tables: `icloud.notes`, `icloud.note_send_queue`
  (`src/server/src/app/server.ts:47-48`).
- Allowed actions: `create_note`, `update_note`, `delete_note`
  (`src/server/src/entities/noteSendItem/types.ts:12`).
- Client routes (`src/server/src/surfaces/client/inotes.ts`):
  - `POST /client/inotes/sync` (line 29)
  - `GET /client/inotes/stats` (line 35)
  - `GET /client/inotes/send-queue/pending` (line 39)
  - `POST /client/inotes/send-queue/:id/result` (line 57)
- Web routes (`src/server/src/surfaces/my/notes.ts`):
  - `GET /my/notes/stats` (line 41)
  - `GET /my/notes/` (line 49)
  - `POST /my/notes/` (line 66)
  - `PUT /my/notes/:id` (line 76)
  - `DELETE /my/notes/:id` (line 88)
- Admin enqueue: `POST /admin/note-send-queue/enqueue`
  (`src/server/src/surfaces/admin/note-send-queue.ts:13`).

## Web surface

- Tab: `/notes` (`web/src/App.tsx:62`).
- API helpers: `web/src/api/notes.ts`.

## AppleScript snippets

Create (`@packages/inotes/Sources/INoteSync/Sender.swift:149-160`):

```applescript
tell application "Notes"
  set targetFolder to first folder whose name is "<folder>"
  set newNote to make new note at targetFolder with properties ¬
    {name:"<name>", body:"<html body>"}
  return id of newNote
end tell
```

Delete (`Sender.swift:181-189`):

```applescript
tell application "Notes"
  delete (every note whose id is "<id>")
end tell
```

All interpolated values pass through
`AppleScriptEscape.quote(_)`
(`@packages/shared/Sources/MacSyncShared/Util/AppleScriptEscape.swift:13`).

## Known limitations

- Attachments and Apple Pencil drawings don't round-trip — title, body,
  folder, and modification date only. See
  [known-limitations](../known-limitations.md#applescript-fragility).
- Formatting fidelity is partial; basic HTML survives, advanced styles can
  flatten.
- Pinned / locked notes: the AppleScript surface does not expose locking
  state; locked notes may be unreadable to the reader.

## Tests (`@packages/inotes/Tests/INoteSyncTests/`)

- `AppleScriptTests.swift` — exercises `Reader.parse(_:)` against canned
  AppleScript output (`@packages/inotes/Sources/INoteSync/Reader.swift:104`),
  and validates the script strings emitted by `scriptForCreate` /
  `scriptForUpdate` / `scriptForDelete`.
- `SenderDispatchTests.swift` — `NoteSender` action routing against a fake
  `NoteApplying` (hermetic).

Not covered: real AppleScript execution, Notes.app side effects, Automation
prompts.
