Skip to content

feat(gmail): add gmail sync-all for concurrent multi-account sync - #1508

Merged
newhoggy merged 5 commits into
mainfrom
issue-1504-gmail-sync-all
Aug 6, 2026
Merged

feat(gmail): add gmail sync-all for concurrent multi-account sync#1508
newhoggy merged 5 commits into
mainfrom
issue-1504-gmail-sync-all

Conversation

@newhoggy

@newhoggy newhoggy commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #1504.

  • Adds omni-dev gmail sync-all, driven by a new .omni-dev/gmail-sync.yaml config file, replacing the external wrapper-script pattern of looping gmail sync --account ... serially over a list of mailboxes.
  • SyncOptions gains an optional shared_pool: Option<Arc<Semaphore>> (None for every existing gmail sync call — behavior/tests unchanged), which sync-all uses to bound in-flight fetch requests across every configured account, layered underneath each account's own local --concurrency. Each account still runs its own independent TokenBucket against its own Gmail quota.
  • Each account resolves its client via the existing helpers::create_client_for, never the --account-driven env var (unsafe across concurrent tasks); --account is rejected outright when combined with sync-all.
  • gmail-sync.yaml is loaded strictly (missing file / empty accounts / malformed YAML / unknown account names are all hard errors before any client is built), unlike scopes.yaml/commit-rules.yaml's warn-and-fall-back loaders.
  • No live per-message progress bars in this release (several concurrent MultiProgress instances would fight over one terminal) — a summary line prints per account as it finishes, plus a trailing combined total; structured -o formats emit one record per account plus a combined_summary.
  • See ADR-0068 for the full design rationale, and docs/gmail.md#sync-all-accounts for the operator-facing docs.

Commit-by-commit:

  1. feat(gmail): thread an optional shared concurrency pool through the sync engine — the engine-level primitive, isolated and behavior-preserving.
  2. feat(gmail): add gmail sync-all for concurrent multi-account sync — the CLI feature itself.
  3. docs(docs,release): add ADR-0068 and document gmail sync-all — ADR, docs, changelog.

Test plan

  • cargo build
  • cargo fmt --check
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo test (full suite) — all pass except the two pre-existing, unrelated daemon_test.rs lifecycle-flake tests (zero references to gmail in that file)
  • cargo insta test --test integration_testhelp_all_output snapshot updated and accepted for the new subcommand
  • omni-dev git commit message lint against the three new commits — 0 errors, 0 warnings

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

Coverage

Total: 97.32% 🔴 -0.01 pp vs main

Comparing 6b01fbb..87bafd2 (merge-base → PR head)

File Before After Δ
src/cli/gmail/sync_all.rs 94.35% 🆕 new
src/cli/gmail.rs 98.46% 98.33% 🔴 -0.14 pp
src/cli/gmail/sync/engine.rs 97.25% 97.52% 🟢 0.27 pp

Patch coverage

Patch: 95.96% (879/916 new lines covered)

File Patch Uncovered new lines
src/cli/gmail.rs 96.15% (50/52) 106, 131
src/cli/gmail/sync.rs 100% (11/11)
src/cli/gmail/sync/engine.rs 100% (176/176)
src/cli/gmail/sync/progress.rs 100% (57/57)
src/cli/gmail/sync_all.rs 94.35% (585/620) 205-206, 208-212, 214-216, 218-219, 221-231, 280-284, 316, 327, 338, 375, 411-412, 414
Uncovered new lines (37)
  • src/cli/gmail.rs:106
  • src/cli/gmail.rs:131
  • src/cli/gmail/sync_all.rs:205
  • src/cli/gmail/sync_all.rs:206
  • src/cli/gmail/sync_all.rs:208
  • src/cli/gmail/sync_all.rs:209
  • src/cli/gmail/sync_all.rs:210
  • src/cli/gmail/sync_all.rs:211
  • src/cli/gmail/sync_all.rs:212
  • src/cli/gmail/sync_all.rs:214
  • src/cli/gmail/sync_all.rs:215
  • src/cli/gmail/sync_all.rs:216
  • src/cli/gmail/sync_all.rs:218
  • src/cli/gmail/sync_all.rs:219
  • src/cli/gmail/sync_all.rs:221
  • src/cli/gmail/sync_all.rs:222
  • src/cli/gmail/sync_all.rs:223
  • src/cli/gmail/sync_all.rs:224
  • src/cli/gmail/sync_all.rs:225
  • src/cli/gmail/sync_all.rs:226
  • src/cli/gmail/sync_all.rs:227
  • src/cli/gmail/sync_all.rs:228
  • src/cli/gmail/sync_all.rs:229
  • src/cli/gmail/sync_all.rs:230
  • src/cli/gmail/sync_all.rs:231
  • src/cli/gmail/sync_all.rs:280
  • src/cli/gmail/sync_all.rs:281
  • src/cli/gmail/sync_all.rs:282
  • src/cli/gmail/sync_all.rs:283
  • src/cli/gmail/sync_all.rs:284
  • src/cli/gmail/sync_all.rs:316
  • src/cli/gmail/sync_all.rs:327
  • src/cli/gmail/sync_all.rs:338
  • src/cli/gmail/sync_all.rs:375
  • src/cli/gmail/sync_all.rs:411
  • src/cli/gmail/sync_all.rs:412
  • src/cli/gmail/sync_all.rs:414

📦 Full per-file coverage summary · run summary

@newhoggy
newhoggy force-pushed the issue-1504-gmail-sync-all branch from c8d338b to 646f841 Compare August 6, 2026 10:17
…ync engine

Adds SyncOptions.shared_pool: Option<Arc<Semaphore>>, acquired
immediately before each message fetch in both fan-out paths
(fetch_and_archive_messages / _streaming), layered underneath each
account's existing local --concurrency clamp. None for every current
caller, so gmail sync's behavior and test suite are unchanged — this
is groundwork for gmail sync-all (#1504, ADR-0067), which will be the
first caller to actually construct Some(pool) and share it across
concurrent per-account syncs.

A dedicated test proves a pool sized to 1 never lets a second fetch
request reach the server while the first is still in flight.
Replaces the external wrapper-script pattern (looping `gmail sync
--account ...` serially over a list of mailboxes) with a first-class
`omni-dev gmail sync-all` subcommand, driven by a new
`.omni-dev/gmail-sync.yaml` config file (src/cli/gmail/sync_all.rs).

- gmail-sync.yaml is discovered via the existing Chain A machinery
  (local/ shadow, --context-dir/OMNI_DEV_CONFIG_DIR, walk-up to repo
  root) and loaded strictly: a missing file, an empty `accounts` list,
  malformed YAML, or an account name absent from
  ~/.omni-dev/settings.json's gmail.accounts map are all hard errors
  before any client is built, with unknown account names batched into
  one message.
- Each account resolves its own client via helpers::create_client_for,
  never GmailCommand::execute's env-var-mutating path, so concurrent
  per-account tasks can't race on OMNI_DEV_GMAIL_ACCOUNT. --account is
  rejected outright when combined with sync-all.
- Accounts run concurrently via tokio::spawn, sharing one
  Arc<Semaphore> (sized by --concurrency / gmail-sync.yaml's
  concurrency / the same default as `gmail sync --concurrency`) built
  on the shared_pool support just added to SyncOptions.
- No live per-message progress bars in this release (several
  concurrent MultiProgress instances would fight over one terminal):
  one summary line prints per account as it finishes, plus a trailing
  combined total. -o json/yaml/yamls/jsonl emit one structured record
  per account plus a combined_summary. Exits non-zero if any account
  fails or reports per-message errors, without aborting the others.

See ADR-0067 for the full design rationale.

Updates the help_all_output golden snapshot for the new subcommand.
Records the design rationale for gmail sync-all's config file, the
shared-semaphore concurrency model, and the v1 decision to skip live
multi-account progress bars (ADR-0068).

- docs/gmail.md gains a "Sync all accounts" section alongside the
  existing single-account Sync docs, cross-referencing Multiple
  accounts and Rate limits and retry behaviour.
- docs/omni-dev-directory.md adds gmail-sync.yaml to the recognised
  files table, a File specs entry, and a Validation behaviour entry
  noting it's the one Chain A file that never silently falls back.
- docs/adrs/README.md's inventory gains the ADR-0068 row.
- CHANGELOG.md's Unreleased/Added section gains an entry for #1504,
  matching the existing Gmail feature entries.
gmail sync-all shipped calling engine::run_sync (no progress channel)
for every account instead of run_sync_with_progress, so a sync-all run
showed no live feedback at all — just each account's summary line once
it finished, unlike gmail sync's own listing-spinner + fetch-bar pair.
ADR-0068 recorded this as a deliberate v1 gap: running one
indicatif::MultiProgress per account concurrently would mean N
independent renderers fighting over one stderr.

Closes that gap with a single shared MultiProgress instead of one per
account:

- SyncProgressBars::new_in(&multi, label) (src/cli/gmail/sync/progress.rs)
  registers a listing-spinner + fetch-bar pair on a caller-supplied
  MultiProgress rather than constructing its own, prefixing both bars
  with an account label so accounts stay visually distinguishable.
  SyncProgressBars::new keeps its existing single-instance behavior and
  plain (unprefixed) template byte-identical to before.
- should_show_progress (src/cli/gmail/sync.rs) is now pub(crate) so
  sync-all gates its bars on the exact same condition gmail sync uses
  (-o table, not --quiet, an interactive stderr) rather than risking a
  second, drifting copy of that logic.
- run_sync_all (src/cli/gmail/sync_all.rs) stands up one MultiProgress
  when that gate passes, gives each account a bar pair registered on
  it, and threads the resulting progress sender through to
  run_sync_with_progress per account. The per-account summary line
  still prints to stdout as each account finishes, now routed through
  MultiProgress::suspend so it doesn't land mid-redraw of the still-live
  bars below it; the trailing combined-total line prints only after
  every render task has drained.
- --quiet's help text now notes it also suppresses the live bars.

Closes #1504 (follow-up)
Updates ADR-0068 and docs/gmail.md to describe the shared-MultiProgress
design that closed Decision 4's v1 gap (live per-account bars for
gmail sync-all).

- ADR-0068's Status line records the same-day amendment; Decision 4 is
  rewritten from "no live multi-account progress bars in v1" to
  describe SyncProgressBars::new_in, the shared should_show_progress
  gate, and the MultiProgress::suspend-wrapped summary print.
  Consequences' "deferred, not solved here" bullet drops the
  now-shipped item, keeping only the still-deferred per-account
  --concurrency override.
- docs/gmail.md's sync-all "Progress and output" section replaces "shows
  no live progress bars" with the shared-bars behavior and notes
  --quiet now suppresses them too.
- CHANGELOG.md's still-unreleased sync-all entry (#1504) is updated in
  place to describe the shipped behavior rather than the interim
  limitation, per Keep a Changelog practice for pre-release amendments.
@newhoggy
newhoggy force-pushed the issue-1504-gmail-sync-all branch from 65eb9bf to 87bafd2 Compare August 6, 2026 11:08
@newhoggy
newhoggy merged commit aa882d5 into main Aug 6, 2026
18 checks passed
@newhoggy
newhoggy deleted the issue-1504-gmail-sync-all branch August 6, 2026 11:19
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.

feat(gmail): add gmail sync-all — concurrent multi-account sync from .omni-dev/gmail-sync.yaml

1 participant