Skip to content

feat: Microsoft 365 and Google connected at the same time, merged review - #63

Merged
urnlahzer merged 2 commits into
mainfrom
feat/dual-provider-review
Oct 5, 2026
Merged

urnlahzer merged 2 commits into
mainfrom
feat/dual-provider-review

Conversation

@urnlahzer

Copy link
Copy Markdown
Owner

What

PR-4, the last slice of docs/plans/2026-09-27-google-provider-and-shared-registration.md: a user can keep a Microsoft 365 account and a Gmail account connected at the same time and review both in one list. Stacked on #62 (Google Tasks); rebase after it merges.

Behaviour

  • Include in scans per provider on the Sources card (the "Mail provider" switch now only picks whose settings you are editing). At least one provider stays enabled. Disabling one clears its session and removes its mail and cards from the review; the other provider's decisions are untouched.
  • One Scan inboxes loads every enabled account in turn. Sign-ins happen back to back. Each provider's result is merged or reported on its own status line; a failure on one side never discards the other side's mail. Retry failed sources per provider.
  • Reminders sync across both services in one job, routed by each record's provider.
  • Title bar: "Signed in · Microsoft 365 + Google" when both are connected.

Verification

  • cargo fmt --all -- --check; clippy -D warnings on both crates: clean
  • cargo test -p openloops-graph --features live-connection: 205 passed
  • cargo test -p openloops-desktop --features native-ui -- --test-threads=1: 479 passed, 0 failed, 3 ignored

Two declared visible changes for Microsoft-only users: the Include in scans checkbox is always shown, and a shared mailbox counts two sources (Inbox, Sent Items) in the progress total when the shared scope is missing. All other Microsoft-only copy and behaviour are byte-identical to #62.

  • Cargo.lock unchanged; public-repo gate passed on the staged files
  • Read-only Opus review; findings addressed as listed in the commit message

Not yet exercised live

No two-account scan has run against real accounts. Smoke test after merge: enable both, sign in to each, scan, confirm cards from both mailboxes with the right "Open message in Outlook/Gmail" labels, then disable one and confirm its cards disappear while the other's decisions persist.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YCesN8ZHnhqa6n3rWFThas

urnlahzer and others added 2 commits October 4, 2026 13:20
…NER REVIEW)

PR-3 of docs/plans/2026-09-27-google-provider-and-shared-registration.md.

Contract edit: docs/adr/ADR-009-reminder-adapters.md gains an additive amendment recording
the Google Tasks adapter and the marker placement both adapters actually use (free-text body:
To Do body.content, Google Tasks notes; never title or due). The reminder checker's ADR hash
literal is re-pinned; P0-REMINDER-CROSS-CONTRACT-001 passes; the pre-existing
P0-REMINDER-INVENTORY-001 failure is unchanged; contracts/*.json untouched.

- graph live/google/tasks.rs: create / complete / check_status against the Google Tasks API
  over the per-provider Google session with the tasks scope. create verifies the signed-in
  account matches the scanned account before any request, takes the first task list as the
  default, posts one task (title, needsAction, a date-only `due`, notes carrying the chosen
  reminder time and the opaque OpenLoops reference) and reports Created only on HTTP 200 with
  a usable id; anything after dispatch without that is the same uncertain outcome the
  Microsoft adapter uses. Every request is origin-confined, bounded, redirect-free, one
  request per attempt. A 401 clears only the Google session slot.
- graph live/provider.rs: create_reminder / complete_reminder / reminder_status dispatch on
  the AccountConfig variant; the Microsoft arms call reminders.rs unchanged.
- desktop: reminder drafts, dispatch, completion and the sync job route by the record's
  provider through account_config(provider); reminder_sync_checks groups by provider so one
  job serves both; the PR-2b "later release" guard is gone; outcome texts name Google Tasks
  for Google records and are unchanged for Microsoft.
- ui: the review draft reads reminder-service and reminder-time-note; for Google the note
  states that Google Tasks saves the date only and does not alert, and that the chosen time
  is written into the task's notes.

Review fixes (Opus read-only review, Opus implementer): `due` is the user's LOCAL calendar
date (an evening pick west of UTC no longer lands on the next day); a 401 on the completion
PATCH clears the Google session and never re-sends; Microsoft copy restored byte-identical
and pinned by tests; Google screens name Google Tasks everywhere ("To Do" / "Google Tasks"
short names keep Microsoft strings unchanged); one shared local-time formatter with UTC
offset; a list response without `items` is an empty list; reminder paths build the Microsoft
config without mail-loading shared/group inputs (reminder_account_config); sync planning
extracted to reminder_sync_jobs() and tested for both providers; facade and adapter guards
reject cross-provider requests; POST non-200 and post-dispatch transport failures are
Uncertain; the PATCH test asserts method and exact body.

Verified: cargo fmt --check; clippy -D warnings (graph live-connection, desktop
native-ui,ui-screenshot); graph 198 passed; desktop 459 passed, 0 failed, 3 ignored;
Cargo.lock unchanged; public-repo gate on staged files.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YCesN8ZHnhqa6n3rWFThas
PR-4 of docs/plans/2026-09-27-google-provider-and-shared-registration.md; the last slice of
the approved plan.

- Settings.mail_providers is an enabled set again: "Include in scans" per provider on the
  Sources card, at least one provider always enabled; disabling a provider clears its session
  and status and removes its messages and cards from the review without touching the other
  provider's decisions. The "Mail provider" Segmented now only selects whose fields the card
  shows.
- One scan job loads every enabled account in sequence (sign-ins run back to back under the
  single in-process guard); each provider's result is merged or reported on its own status
  line, so one provider's failure never discards the other's mail. LoadProgress totals span
  both providers. Failed sources are keyed (provider, label) and retried through their own
  AccountConfig. Incremental checks run every enabled provider.
- Reminder sync runs one job over both providers' records (routing by record provider from
  PR-3); the PR-2b Microsoft-only guard is gone.
- Title bar names every connected provider; the Sources heading shows a pill per connected
  provider when more than one is enabled; scope summary wording covers both.

Review fixes (Opus read-only review, Opus implementer): the Review pane follows the first
enabled provider (primary_mail_provider), not the Sources card's view selection, so a
Microsoft-only user who looks at the Google segment keeps Microsoft To Do copy and reconcile
routing; the handle-uniqueness test now fails without the next_handle fix; remove_provider
resets scan_summary / scan_errors / scan_incomplete; retry_jobs skips a provider it cannot
configure with a status line instead of failing the retry; stale "<service>: <error>" lines
clear when that provider loads; the dead own-inbox-accessible property is gone; loaders
document that LoadProgress::begin runs once per job. Declared visible changes for
Microsoft-only users: the "Include in scans" checkbox is always shown, and a shared mailbox
counts two sources (Inbox, Sent Items) in the progress total when the shared scope is
missing (done still reaches total; tested). Follow-ups recorded in the plan: cross-provider
anchors on surviving cards after a removal; "Reload saved settings" changing the enabled set
without clearing.

Verified: cargo fmt --check; clippy -D warnings (graph live-connection, desktop
native-ui,ui-screenshot); graph 205 passed; desktop 479 passed, 0 failed, 3 ignored;
Cargo.lock unchanged; public-repo gate on staged files.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YCesN8ZHnhqa6n3rWFThas
@urnlahzer
urnlahzer merged commit 53a4871 into main Oct 5, 2026
2 checks passed
@urnlahzer
urnlahzer deleted the feat/dual-provider-review branch October 5, 2026 13:07
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