Skip to content

Multi-account support and correct live-store selection#4

Open
joshthomp wants to merge 1 commit into
viticci:mainfrom
joshthomp:fix/find-main-db-path-selection
Open

Multi-account support and correct live-store selection#4
joshthomp wants to merge 1 commit into
viticci:mainfrom
joshthomp:fix/find-main-db-path-selection

Conversation

@joshthomp

Copy link
Copy Markdown

Summary

RemCTL currently reads from a single Reminders store chosen by file size.
On setups with more than one account (iCloud + Exchange/CalDAV + on-device),
the largest file is often a bloated Exchange store rather than the live
iCloud one, so RemCTL silently operates on the wrong account.

This PR fixes store selection and, on top of that, adds opt-in
multi-account support: read from every connected account, see which account
each item belongs to, and create/modify/delete in any of them. The default
behavior is unchanged and single-account output is byte-for-byte identical.

Motivation

find_main_db_path() selected the store with the largest file on disk.
Exchange stores accumulate size independent of how many live reminders they
hold, so the heuristic can pick a stale/secondary account. The result is
confusing: today, lists, etc. show an account the user didn't expect.

What changed

Store selection

  • Choose the active store by reminder count, then file-group recency.
  • REMCTL_DB env var / dbPath config key to pin a specific file;
    REMCTL_STORE_DIR / storeDir to point at a Stores directory.

Reading across accounts (opt-in)

  • --all-accounts includes every connected account; --account NAME
    restricts to one. A personal default can be set with
    REMCTL_ACCOUNT_SCOPE or the accountScope config key (all or a name).
  • Source labelling: bold per-account headers (human), account /
    accountType fields (JSON), an Account column (table).
  • IDs (Z_PK) are per-account; when the scope spans accounts, an ID or
    list name found in more than one produces a disambiguation error that
    lists candidates and asks for --account.
  • New accounts command (lists connected accounts) and config command
    (accountScope / storeDir / dbPath).

Writing to a specific account

  • The EventKit bridge gains a list_calendars action and targets lists by
    stable calendar identifier, so a (list, account) pair resolves to the
    right calendar instead of an arbitrarily-chosen same-named list.
  • --account on add / done / edit / delete / flag, etc.
  • ⚠️ The bridge must be recompiled after pulling this change:
    ./install.sh (runs swiftc -O).

Correctness fix

  • stats counted overdue against the current time, so reminders due earlier
    today were reported as overdue even though overdue itself lists none.
    It now counts from the start of the day, matching overdue and today.

Deliberate scope limit

  • export / import operate on a single account (reminder IDs collide
    across accounts). They accept --account but refuse an all-accounts
    scope with a clear error rather than writing a silently partial backup.

Backward compatibility

Single-account behavior is the default. With no flags/config, output is
byte-for-byte identical to the previous release — verified by diffing every
read command × format (plain/table/json) against main, for both the
default scope and an explicit --account. The only intended output change
is the stats overdue fix above.

Testing

  • python3 -m pytest tests/test_cli.py -> 273 passing, plus coverage for
    account discovery, scope precedence, cross-account addressing/ambiguity,
    and single-account back-compat.
  • One pre-existing failure,
    test_info_json_keeps_due_date_separate_from_display_alarm_date, is
    not introduced here — it fails identically on unmodified main
    because it hard-codes a UTC+2 expected value and only passes in that
    timezone. Happy to fix it (force a fixed TZ) in a separate change if you'd
    like.

Notes for review

  • One PR intentionally bundles the Python CLI and the Swift bridge because
    account-targeted writes need both.
  • No third-party dependencies added.

Reminders keeps each account in its own SQLite store. RemCTL picked the
store by file size, which on multi-account setups selected a bloated
Exchange store instead of the live iCloud one — so the wrong account's
reminders were shown. Select the active store by reminder count and
file-group recency instead, with a REMCTL_DB env / config `dbPath`
override for pinning a specific file.

Building on that, add opt-in multi-account support. The default stays
single-account and its output is byte-for-byte unchanged:

- `--all-accounts` reads every connected account; `--account NAME` scopes
  to one. A default scope can be set via the REMCTL_ACCOUNT_SCOPE env var
  or the `accountScope` config key ("all" or an account name).
- Multi-account output labels each item by source: bold per-account
  headers in human output, `account`/`accountType` fields in JSON, and an
  Account column in tables.
- Reminder IDs (Z_PK) are per-account. When the scope spans more than one
  account, a bare ID or list name present in multiple accounts produces a
  disambiguation error and requires `--account`.
- New `accounts` command lists connected accounts; new `config` command
  gets/sets `accountScope`, `storeDir`, and `dbPath`.
- Writes (add/done/edit/delete/flag/...) can target any connected account.
  The EventKit bridge gains a `list_calendars` action and calendar-identifier
  targeting, so a (list, account) pair maps to a stable calendar rather than
  an arbitrary same-named list in another account.
  NOTE: the bridge must be recompiled via install.sh.
- export/import stay single-account by design (IDs collide across
  accounts): they accept `--account` but refuse an all-accounts scope
  rather than silently writing a partial backup.

Also fix `stats` overdue counting, which compared against the current
time and so counted reminders due earlier today as overdue — disagreeing
with the `overdue` command, which lists none. Count from the start of day
so the two agree.

Docs updated; adds multi-account test coverage (discovery, scope
resolution, addressing, and single-account back-compat).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant