Files
pad/docs
xarmian fbabb65869 feat(schedule): carry schedules in workspace export/import and migrate-to-pg (TASK-4104 D2) (#2360)
* feat(schedule): carry schedules in workspace export/import and migrate-to-pg (TASK-4104 D2)

WorkspaceExport.Schedules (additive, omitempty, no version bump) carries each
live item's schedule with its occurrence history and managed reminder state.
Import re-compiles every rule, stores canonical timing with rdates/exdates
re-marshalled from parsed walls, rebuilds exactly one managed reminder per live
schedule, recomputes next_at/remind_at, and remaps history comment ids.
A malformed schedule is dropped, counted and reported (dropped_schedules) while
the item and the import survive. insertCarriedScheduleTx is the shared helper
the cross-workspace copy (D3) will reuse.

Claude-Session: https://claude.ai/code/session_01Pa22hHpfXoZbQkEFvPtNeS

* feat(schedule): list the schedule tables in MigratedTables (TASK-4104 D2)

The migration now reads item_schedules and schedule_occurrences, so the NUL
preflight must refuse on them; the export-coverage guard maps the new section.

Claude-Session: https://claude.ai/code/session_01Pa22hHpfXoZbQkEFvPtNeS

* fix(schedule): qualify the schedule export ORDER BY for Postgres (TASK-4104 D2)

scheduleColumns also selects the managed reminder's id, so a bare ORDER BY id is
ambiguous on Postgres (SQLSTATE 42702) and failed every workspace export there.

Claude-Session: https://claude.ai/code/session_01Pa22hHpfXoZbQkEFvPtNeS

* test(schedule): every schedule state round-trips through export/import unchanged (TASK-4104 D2-a evidence)

Claude-Session: https://claude.ai/code/session_01Pa22hHpfXoZbQkEFvPtNeS

* fix(schedule): migrate-to-pg reports dropped schedules; anchor_wall all-day check; comment map guards (TASK-4104 D2 r1-1, r1-2, r1-3)

migrate-to-pg prints each refused schedule as it happens and a closing summary naming the SQLite source; print-only, exit 0. An anchor_wall whose all-day form differs from dtstart's is dropped. The import's comment map skips an empty id and binds an occurrence only to a comment of its own item. A schedule is marked seen only after a successful insert. Documents the dropped_schedules reason codes.

Claude-Session: https://claude.ai/code/session_01Pa22hHpfXoZbQkEFvPtNeS

* fix(cli): migrate-to-pg's drop summary says how to read the uncarried rows (TASK-4104 D2 r2-2)

The summary said dropped schedules "remain in <from>", but the migrate
then marks that file as migrated and pad refuses to open it. It now says
the rows are still there, that pad won't open the file, and how to read
them: sqlite3 on item_schedules and schedule_occurrences, or a `pad db
backup` copy taken before the migration. The red row pins the wording.

Claude-Session: https://claude.ai/code/session_01Pa22hHpfXoZbQkEFvPtNeS

* fix(cli): quote the source path in migrate-to-pg's sqlite3 hint (TASK-4104 D2 r3-1)

The drop summary's "read them with sqlite3 <from>" is a command an
operator pastes, and a macOS data path ("Application Support") has a
space. The path is now single-quoted for a POSIX shell, with an
embedded ' written as '\''. The new row sends a path with a space and a
quote through sh -c and gets it back unchanged.

Claude-Session: https://claude.ai/code/session_01Pa22hHpfXoZbQkEFvPtNeS
2026-11-01 00:02:42 -04:00
..